package com.miplus.ccrm.core.service.crd;


import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.ObjectUtils;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.github.yulichang.base.MPJBaseServiceImpl;
import com.github.yulichang.wrapper.MPJLambdaWrapper;
import com.miplus.ccrm.common.*;
import com.miplus.ccrm.core.controller.vo.credit.*;
import com.miplus.ccrm.core.mapper.*;
import com.miplus.ccrm.core.pojo.*;
import com.miplus.ccrm.core.pojo.model.AutoDistribute;
import com.miplus.ccrm.core.security.JCommonData;
import com.miplus.ccrm.core.service.ExportService;
import com.miplus.ccrm.core.service.action.FiAmortService;
import com.miplus.ccrm.core.service.action.FiDelayService;
import com.miplus.ccrm.core.service.conf.FiDistbatchHistService;
import com.miplus.ccrm.core.service.conf.IouPorductApiService;
import com.miplus.ccrm.core.service.conf.MsgAlertsService;
import com.miplus.ccrm.core.service.cust.ConCustomertagService;
import com.miplus.ccrm.core.service.strategy.CfgActivityOthergrplinkService;
import com.miplus.ccrm.core.utils.EnumConfirmStatus;
import com.miplus.ccrm.core.utils.EnumFiCaseQueue;
import com.miplus.ccrm.core.utils.EnumFiCaseStatus;
import com.miplus.ccrm.core.utils.EnumRuleStepAction;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.cursor.Cursor;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.math.BigDecimal;
import java.text.DecimalFormat;
import java.text.SimpleDateFormat;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * @author pc
 * @description 针对表【fi_creditcases(案件表)】的数据库操作Service实现
 * @createDate 2024-03-26 21:59:40
 */
@Slf4j
@Service
public class FiCreditcasesServiceImpl extends MPJBaseServiceImpl<FiCreditcasesMapper, FiCreditcases>
        implements FiCreditcasesService{
    @Resource
    private ConCustomertagMapper conCustomertagMapper;
    @Resource
    private ConCustomertagService conCustomertagService;

    @Resource
    private ConCompanyInfoService conCompanyInfoService;
    @Resource
    private FiDistbatchHistService fiDistbatchHistService;
    @Resource
    private FiDelayService fiDelayService;

    @Resource
    private FiCreditcasesTagMapper fiCreditcasesTagMapper;

    @Resource
    private MsgAlertsService msgAlertsService;
    @Resource
    private CreditAccountBalanceMapper creditAccountBalanceMapper;
    @Resource
    private CreditAccountMapper creditAccountMapper;
    @Resource
    private CreditAccountNoutbillMapper accountNoutbillMapper;
    @Resource
    private JCommonData jCommonData;
    @Resource
    private FiAmortService fiAmortService;
    @Resource
    private ExportService exportService;
    @Resource
    private FiImportHistService fiImportHistService;

    @Resource
    private FiCreditcaseGrpTagService fiCreditcaseGrpTagService;

    @Resource
    CfgActivityOthergrplinkService cfgActivityOthergrplinkService;

    @Resource
    IouPorductApiService iouPorductApiService;

    @Resource
    IouLoanInvoiceInfoService iouLoanInvoiceInfoService;

    @Resource
    FiCreditcaseAssistantMapper fiCreditcaseAssistantMapper;

    //预分案或分配操作
    private static final int  PRE_OPERATE=1;

    //分案/分配模式=3，按案件比例分案
    private static final int DIST_MODE_CASERADIO=3;

    //分案/分配模式=2，按金额比例分案
    private static final int DIST_MODE_AMTRADIO=2;

    //回收到组
    private static final int RECY_MODE_GROUP=1;

    //回收到案件总池
    private static final int RECY_MODE_TOP=2;

    private MPJLambdaWrapper getQueryWrapper(JSysUser user,CrdCaseReqVo paras)
    {
        String noinctags="";
        String noincCaseTags="";
        String $usrid=user.getUsrid();
        String tags= JPubUtils.getInString(paras.getCusttaglist());
        String caseTags= JPubUtils.getInString(paras.getCasetaglist());
        String caseGrpTags = JPubUtils.getInString(paras.getCaseGrpTag());
        String customerGrpTags = JPubUtils.getInString(paras.getCustGrpTag());
        String cardnos=JPubUtils.getInString(paras.getCardnolist());
        if(JPubUtils.IsNotEmpty(tags)){
            noinctags = JPubUtils.BoolToString(paras.getNoincludetags());
        }
        if(JPubUtils.IsNotEmpty(caseTags)){
            noincCaseTags = JPubUtils.BoolToString(paras.getNoincludecasetags());
        }
        String noincaccts="";
        if(JPubUtils.IsNotEmpty(paras.getAcctstatus())){
            noincaccts = JPubUtils.BoolToString(paras.getNoincludeaccs());
        }

        //增加对批量电话查询的处理
        String custphones = "";
        if(JPubUtils.IsNotEmpty(paras.getCustphonelist())){
            custphones = JPubUtils.getInString(paras.getCustphonelist());
        }
        String custphoneTypeList="";
        if(JPubUtils.IsNotEmpty(paras.getCustphoneTypeList())){
            custphoneTypeList=JPubUtils.getInString(paras.getCustphoneTypeList());
        }

        //2024-07-24
        boolean viewAllCase=JUsrPerm.viewAllFiCase(user);
        //数据库尽量不要使用函数，否则慢,尤其TDSQL
        //.apply("fi_hasFiCaseView({0},t.creditcaseid,t.status,t.sendtodept,t.sendtousr)>0", $usrid)
        //      log.info("getQueryWrapper:viewAllCase={},iscock={}",viewAllCase,iscock);
        boolean viewGrpFiCase = false;
        if(!viewAllCase && user.getMgrgrplist().size()>0)
                viewGrpFiCase = true;
        return new MPJLambdaWrapper<FiCreditcases>()
                .innerJoin(ConCustomer.class, "cu", ConCustomer::getCustomernumber, FiCreditcases::getCustomernumber)
                //  .leftJoin(CreditAccount.class, "cda", CreditAccount::getAcct, FiCreditcases::getCreditAcct)
                //  .leftJoin(CreditAccountBalance.class, "cdb", CreditAccountBalance::getAcct, FiCreditcases::getCreditAcct)
                .leftJoin(FiTask.class, "tk", FiTask::getTasknumber, FiCreditcases::getLastcctaskid)
//                .leftJoin(FiCreditcaseGrpTag.class,"fgt",FiCreditcaseGrpTag::getCreditcaseid, FiCreditcases::getCreditcaseid)
//                .leftJoin(ConCustomerGrpTag.class,"cgt",ConCustomerGrpTag::getCustomernumber, FiCreditcases::getCustomernumber)
                .leftJoin(ConBlacklist.class,"black",ConBlacklist::getCustomernumber, FiCreditcases::getCustomernumber)
//                .leftJoin(ConCompanyInfo.class,"comp",ConCompanyInfo::getComUserId, FiCreditcases::getCustomernumber)
//                .leftJoin(IouLoanInvoiceInfo.class,"il",IouLoanInvoiceInfo::getCreditcaseid, FiCreditcases::getCreditcaseid)
//                .groupBy(FiCreditcases::getCreditcaseid)
                .leftJoin("iou_product pr on pr.product_id = t.product_id and ifnull(pr.merchant_id,'') = ifnull(t.merchant_id,'')")
                .leftJoin("fi_creditcase_assistant fca on fca.ficaseid = t.creditcaseid and fca.status = 1")
                .in(viewGrpFiCase, FiCreditcases::getSendtodept, user.getMgrgrplist())
                .apply(!viewAllCase && !viewGrpFiCase,getPuvViewSql($usrid))
//                .in(JPubUtils.IsNotEmpty(paras.getCaseGrpTag()),FiCreditcaseGrpTag::getGrpTag,paras.getCaseGrpTag())
//                .in(JPubUtils.IsNotEmpty(paras.getCustGrpTag()),ConCustomerGrpTag::getGrpTag,paras.getCustGrpTag())
                .in(JPubUtils.IsNotEmpty(paras.getDistributorid()),FiCreditcases::getDistributorid,paras.getDistributorid())
                .in(JPubUtils.IsNotEmpty(paras.getAssignid()),FiCreditcases::getAssignid,paras.getAssignid())
                .in(JPubUtils.IsNotEmpty(paras.getDisttag()),FiCreditcases::getDisttag,paras.getDisttag())
                //客户编码查询。
                .eq(JPubUtils.IsNotEmpty(paras.getCustnumber()), ConCustomer::getCustomernumber, paras.getCustnumber())
//                .eq(JPubUtils.IsNotEmpty(paras.getEntpName()), ConCompanyInfo::getEntpName, paras.getEntpName())
                //客户类型
                .eq(JPubUtils.IsNotEmpty(paras.getCusttype()), ConCustomer::getType, paras.getCusttype())
                //客户名称
                .likeRight(JPubUtils.IsNotEmpty(paras.getCustname()), ConCustomer::getCustname, paras.getCustname())
                //客户证件号
                .eq(JPubUtils.IsNotEmpty(paras.getOpenacctcertno()), ConCustomer::getOpenacctcertno, paras.getOpenacctcertno())
                //风险等级
                .eq(JPubUtils.IsNotEmpty(paras.getRisklevel()), ConCustomer::getRisklevel, paras.getRisklevel())

                //客户逾期总金额
                .ge(JPubUtils.IsNotEmpty(paras.getPrododudtotalPrin1()), ConCustomer::getPrododudtotalPrin, paras.getPrododudtotalPrin1())
                .le(JPubUtils.IsNotEmpty(paras.getPrododudtotalPrin2()), ConCustomer::getPrododudtotalPrin, paras.getPrododudtotalPrin2())
                //客户逾期天数
                .ge(JPubUtils.IsNotEmpty(paras.getOverdueDays1()), ConCustomer::getOverdueDays, paras.getOverdueDays1())
                .le(JPubUtils.IsNotEmpty(paras.getOverdueDays2()), ConCustomer::getOverdueDays, paras.getOverdueDays2())
                //案件应催未还金额
                .ge(JPubUtils.IsNotEmpty(paras.getWaitAmount1()), FiCreditcases::getWaitAmount, paras.getWaitAmount1())
                .le(JPubUtils.IsNotEmpty(paras.getWaitAmount2()), FiCreditcases::getWaitAmount, paras.getWaitAmount2())

                //案件字段
                .eq(JPubUtils.IsNotEmpty(paras.getCreditcaseid()), FiCreditcases::getCreditcaseid, paras.getCreditcaseid())
                //案件状态查询。
                .in(JPubUtils.IsNotEmpty(paras.getStatus()), FiCreditcases::getStatus, paras.getStatus())
                //案件合同号
                .eq(JPubUtils.IsNotEmpty(paras.getAcctno()), FiCreditcases::getCreditAcct, paras.getAcctno())
//                //产品ID
//                .eq(JPubUtlis.IsNotEmpty(paras.getProductId()), FiCreditcases::getProductId, paras.getProductId())
                //产品列表主键 ID
                .in(JPubUtils.IsNotEmpty(paras.getProductName()), "pr.id", paras.getProductName())

//                //商户名称
//                .in(JPubUtils.IsNotEmpty(paras.getMerchantName()),"pr.merchant_name", paras.getMerchantName())

                // 商户号 merchantId
                .eq(JPubUtils.IsNotEmpty(paras.getMerchantId()), FiCreditcases::getMerchantId, paras.getMerchantId())
                //借据号
                .exists(JPubUtils.IsNotEmpty(paras.getLoanInvoiceId()), String.format("select 1 from iou_loan_invoice_info ili where ili.creditcaseid = t.creditcaseid and ili.loan_invoice_id = '%s'",paras.getLoanInvoiceId()))
                // 周期编号
                .eq(JPubUtils.IsNotEmpty(paras.getSerialNumber()), FiCreditcases::getSerialNumber, paras.getSerialNumber())

                //催收机构
                .in(paras.getSendtodept()!=null && paras.getSendtodept().size()>0, FiCreditcases::getSendtodept, paras.getSendtodept())
                .apply(paras.getSendtodept()!=null && paras.getSendtodept().size()==0,"ifnull(t.sendtodept,'')='' ")
                //催收员
                .in(JPubUtils.IsNotEmpty(paras.getSendtousr()), FiCreditcases::getSendtousr, paras.getSendtousr())
                //停止呼叫的类型
                .apply(JPubUtils.IsEqual(paras.getCcc(), "S"), getStopCallSql())
                //队列状态
                .eq(JPubUtils.IsNotEqualAndNotNull(paras.getCcc(), "S"), FiCreditcases::getCcc, paras.getCcc())
                .notExists(JPubUtils.IsNotEqualAndNotNull(paras.getCcc(), "S"), getNoDeadSql())
                // 分案批次
                .eq(JPubUtils.IsNotEmpty(paras.getDistbatchid()), FiCreditcases::getDistbatchid, paras.getDistbatchid())

                // .eq(JPubUtils.IsNotEmpty(paras.getAccttype()), CreditAccount::getAcctType, paras.getAccttype())
                .eq(JPubUtils.IsNotEmpty(paras.getBatchid()), FiCreditcases::getBatchid, paras.getBatchid())
                .eq(JPubUtils.IsNotEmpty(paras.getAssignbatchid()), FiCreditcases::getAssignbatchid, paras.getAssignbatchid())
                .eq(JPubUtils.IsNotEmpty(paras.getDistributetype()), FiCreditcases::getDistributetype, paras.getDistributetype())
                .eq(JPubUtils.IsNotEmpty(paras.getAssigntype()), FiCreditcases::getAssigntype, paras.getAssigntype())

//                .between(JPubUtils.IsNotEmpty(paras.getStartdt()), FiCreditcases::getCrdt, JPubUtils.StringToDate(paras.getStartdt()), JPubUtils.StringToDate(paras.getEnddt()))
//                .between(JPubUtils.IsNotEmpty(paras.getDiststartdt()), FiCreditcases::getDistdt, JPubUtils.StringToDate(paras.getDiststartdt()), JPubUtils.StringToDate(paras.getDistenddt()))
//                .between(JPubUtils.IsNotEmpty(paras.getInstartdt()), FiCreditcases::getIndt, JPubUtils.StringToDate(paras.getInstartdt()), JPubUtils.StringToDate(paras.getInenddt()))
//                .between(JPubUtils.IsNotEmpty(paras.getRecystartdt()), FiCreditcases::getRecycledt, JPubUtils.StringToDate(paras.getRecystartdt()), JPubUtils.StringToDate(paras.getRecyenddt()))
//                .between(JPubUtils.IsNotEmpty(paras.getLastccstartdt()), FiCreditcases::getLastccdt, JPubUtils.StringToDate(paras.getLastccstartdt()), JPubUtils.StringToDate(paras.getLastccenddt()))
//                .between(JPubUtils.IsNotEmpty(paras.getCpTotalamt1()),FiCreditcases::getCpTotalamt,paras.getCpTotalamt1(),paras.getCpTotalamt2())
//                .between(JPubUtils.IsNotEmpty(paras.getCpOverdueDays1()),FiCreditcases::getCpOverdueDays,paras.getCpOverdueDays1(),paras.getCpOverdueDays2())
//                .between(JPubUtils.IsNotEmpty(paras.getRemindRepayPrincipal1()),FiCreditcases::getRemindRepayPrincipal,paras.getRemindRepayPrincipal1(),paras.getRemindRepayPrincipal2())
//                .between(JPubUtils.IsNotEmpty(paras.getRepaidAmount1()),FiCreditcases::getRepaidAmount,paras.getRepaidAmount1(),paras.getRepaidAmount2())
//                .between(JPubUtils.IsNotEmpty(paras.getRemindRepayDays1()),FiCreditcases::getRemindRepayDays,paras.getRemindRepayDays1(),paras.getRemindRepayDays2())

                .ge(JPubUtils.IsNotEmpty(paras.getStartdt()), FiCreditcases::getCrdt, paras.getStartdt())
                .le(JPubUtils.IsNotEmpty(paras.getEnddt()), FiCreditcases::getCrdt, paras.getEnddt())
                .ge(JPubUtils.IsNotEmpty(paras.getDiststartdt()), FiCreditcases::getDistdt, paras.getDiststartdt())
                .le(JPubUtils.IsNotEmpty(paras.getDistenddt()), FiCreditcases::getDistdt, paras.getDistenddt())
                .ge(JPubUtils.IsNotEmpty(paras.getInstartdt()), FiCreditcases::getIndt, paras.getInstartdt())
                .le(JPubUtils.IsNotEmpty(paras.getInenddt()), FiCreditcases::getIndt, paras.getInenddt())
                .ge(JPubUtils.IsNotEmpty(paras.getRecystartdt()), FiCreditcases::getRecycledt, paras.getRecystartdt())
                .le(JPubUtils.IsNotEmpty(paras.getRecyenddt()), FiCreditcases::getRecycledt, paras.getRecyenddt())
                .ge(JPubUtils.IsNotEmpty(paras.getLastccstartdt()), FiCreditcases::getLastccdt, paras.getLastccstartdt())
                .le(JPubUtils.IsNotEmpty(paras.getLastccenddt()), FiCreditcases::getLastccdt, paras.getLastccenddt())
                .ge(JPubUtils.IsNotEmpty(paras.getCpTotalamt1()), FiCreditcases::getCpTotalamt, paras.getCpTotalamt1())
                .le(JPubUtils.IsNotEmpty(paras.getCpTotalamt2()), FiCreditcases::getCpTotalamt, paras.getCpTotalamt2())
                .ge(JPubUtils.IsNotEmpty(paras.getCpOverdueDays1()), FiCreditcases::getCpOverdueDays, paras.getCpOverdueDays1())
                .le(JPubUtils.IsNotEmpty(paras.getCpOverdueDays2()), FiCreditcases::getCpOverdueDays, paras.getCpOverdueDays2())
                .ge(JPubUtils.IsNotEmpty(paras.getRemindRepayPrincipal1()), FiCreditcases::getRemindRepayPrincipal, paras.getRemindRepayPrincipal1())
                .le(JPubUtils.IsNotEmpty(paras.getRemindRepayPrincipal2()), FiCreditcases::getRemindRepayPrincipal, paras.getRemindRepayPrincipal2())
                .ge(JPubUtils.IsNotEmpty(paras.getRepaidAmount1()), FiCreditcases::getRepaidAmount, paras.getRepaidAmount1())
                .le(JPubUtils.IsNotEmpty(paras.getRepaidAmount2()), FiCreditcases::getRepaidAmount, paras.getRepaidAmount2())
                .ge(JPubUtils.IsNotEmpty(paras.getRemindRepayDays1()), FiCreditcases::getRemindRepayDays, paras.getRemindRepayDays1())
                .le(JPubUtils.IsNotEmpty(paras.getRemindRepayDays2()), FiCreditcases::getRemindRepayDays, paras.getRemindRepayDays2())


                .ge(JPubUtils.IsNotEmpty(paras.getAppointstartdt()), FiCreditcases::getAppointdt, paras.getAppointstartdt())
                .le(JPubUtils.IsNotEmpty(paras.getAppointenddt()), FiCreditcases::getAppointdt, paras.getAppointenddt())
                .ge(JPubUtils.IsNotEmpty(paras.getRecentlyRepTime1()), FiCreditcases::getRecentlyRepTime, paras.getRecentlyRepTime1())
                .le(JPubUtils.IsNotEmpty(paras.getRecentlyRepTime2()), FiCreditcases::getRecentlyRepTime, paras.getRecentlyRepTime2())
                .ge(JPubUtils.IsNotEmpty(paras.getCpEndTime1()), FiCreditcases::getCpEndTime, paras.getCpEndTime1())
                .le(JPubUtils.IsNotEmpty(paras.getCpEndTime2()), FiCreditcases::getCpEndTime, paras.getCpEndTime2())
                .ge(JPubUtils.IsNotEmpty(paras.getCpStartTime1()), FiCreditcases::getCpStartTime, paras.getCpStartTime1())
                .le(JPubUtils.IsNotEmpty(paras.getCpStartTime2()), FiCreditcases::getCpStartTime, paras.getCpStartTime2())
                .ge(JPubUtils.IsNotEmpty(paras.getOdudPrin1()), FiCreditcases::getOverdueTotalamt, paras.getOdudPrin1())
                .le(JPubUtils.IsNotEmpty(paras.getOdudPrin2()), FiCreditcases::getOverdueTotalamt, paras.getOdudPrin2())
                .ge(JPubUtils.IsNotEmpty(paras.getStatusdt1()), FiCreditcases::getStatusdt, paras.getStatusdt1())
                .le(JPubUtils.IsNotEmpty(paras.getStatusdt2()), FiCreditcases::getStatusdt, paras.getStatusdt2())
                .ge(JPubUtils.IsNotEmpty(paras.getMthsodue1()), FiCreditcases::getMthsOdue, paras.getMthsodue1())
                .le(JPubUtils.IsNotEmpty(paras.getMthsodue2()), FiCreditcases::getMthsOdue, paras.getMthsodue2())
                .in(JPubUtils.IsNotEmpty(paras.getOverdueStage()),FiCreditcases::getOverdueStage,paras.getOverdueStage())
                .ge(JPubUtils.IsNotEmpty(paras.getMthsday1()), FiCreditcases::getMthsDay, paras.getMthsday1())
                .le(JPubUtils.IsNotEmpty(paras.getMthsday2()), FiCreditcases::getMthsDay, paras.getMthsday2())
                .in(JPubUtils.IsNotEmpty(paras.getCreditcaseidlist()),FiCreditcases::getCreditcaseid,paras.getCreditcaseidlist())
                .in(JPubUtils.IsNotEmpty(paras.getOpenacctcertnolist()), ConCustomer::getOpenacctcertno, paras.getOpenacctcertnolist())
                .in(JPubUtils.IsNotEmpty(paras.getCustnumberlist()), ConCustomer::getCustomernumber, paras.getCustnumberlist())
                .in(JPubUtils.IsNotEmpty(paras.getSerialnumberlist()),FiCreditcases::getSerialNumber,paras.getSerialnumberlist())
                .in(JPubUtils.IsNotEmpty(paras.getQueuelist()),FiCreditcases::getCcc, paras.getQueuelist())
                //  .eq(JPubUtils.IsNotEmpty(paras.getIsamortcust()), CreditAccount::getIsAmortCust, paras.getIsamortcust())
                //     .eq(JPubUtils.IsNotEmpty(paras.getBilldate()),CreditAccount::getBillDate,paras.getBilldate())
                //     .eq(JPubUtils.IsNotEmpty(paras.getCurrency()),CreditAccount::getCurrency,paras.getCurrency())
                //     .in(JPubUtils.IsNotEmpty(paras.getAcctFlevel()),CreditAccount::getAcctFlevel,paras.getAcctFlevel())
                .apply(JPubUtils.IsEqual(paras.getIsfiamort(), "1"), "fc_hasFiAmort(t.creditcaseid)>0")
                .apply(JPubUtils.IsEqual(paras.getIsfiamort(), "0"), "fc_hasFiAmort(t.creditcaseid)=0")
                // .in(JPubUtils.IsEqual(noincaccts, 0), CreditAccount::getAcctStatus, paras.getAcctstatus())
                //  .notIn(JPubUtils.IsEqual(noincaccts, 1), CreditAccount::getAcctStatus, paras.getAcctstatus())
                //  .exists(JPubUtils.IsNotEmpty(cardnos), String.format("select 1 from credit_card crd where crd.acct=t.credit_acct and crd.card_no in ('%s')", cardnos))
                //   .exists(JPubUtils.IsNotEmpty(paras.getCardno()), String.format("select 1 from credit_card crd where crd.acct=t.credit_acct and crd.card_no='%s'", paras.getCardno()))
                .apply(JPubUtils.IsEqual(noinctags, 0),String.format("cu.customernumber in (select tag.custid from con_customertag tag where tag.custid=t.customernumber and tag.id in ('%s'))", tags))
                .apply(JPubUtils.IsEqual(noinctags, 1), String.format("cu.customernumber not in (select tag.custid from con_customertag tag where tag.custid=t.customernumber and tag.id in ('%s'))", tags))
                .apply(JPubUtils.IsEqual(noincCaseTags, 0),String.format("t.creditcaseid in (select caseTag.creditcaseid from fi_creditcases_tag caseTag where caseTag.creditcaseid=t.creditcaseid and caseTag.id in ('%s'))", caseTags))
                .apply(JPubUtils.IsEqual(noincCaseTags, 1),String.format("t.creditcaseid not in (select caseTag.creditcaseid from fi_creditcases_tag caseTag where caseTag.creditcaseid=t.creditcaseid and caseTag.id in ('%s'))", caseTags))
                .exists(JPubUtils.IsNotEmpty(paras.getCustphone())&&custphoneTypeList.length()>0 , String.format("select 1 from con_contact ct where ct.customernumber=t.customernumber and ct.phone='%s' and ct.relational in ('%s')",paras.getCustphone(),custphoneTypeList))
                .exists(JPubUtils.IsNotEmpty(paras.getCustphone())&&custphoneTypeList.length()==0, String.format("select 1 from con_contact ct where ct.customernumber=t.customernumber and ct.phone='%s'",paras.getCustphone()))
                .exists(StringUtils.isNotEmpty(custphones),String.format("select 1 from con_contact ct where ct.customernumber=t.customernumber and ct.phone and ct.phone in ('%s') ",custphones))
                .eq(StringUtils.isNotEmpty(paras.getBlacklist()),ConBlacklist::getType,paras.getBlacklist())
               // .exists(StringUtils.isNotEmpty(paras.getBlacklist()) && "1".equals(paras.getBlacklist())," SELECT 1 FROM con_blacklist cb WHERE cb.type=1 AND cb.customernumber=t.customernumber")
               // .exists(StringUtils.isNotEmpty(paras.getBlacklist()) && "2".equals(paras.getBlacklist())," SELECT 1 FROM con_blacklist cb WHERE cb.type=2 AND cb.customernumber=t.customernumber")
                .apply(JPubUtils.IsEqual(paras.getIscodebt(),"1"),"(select count(1) from fi_creditcases fi where fi.customernumber=t.customernumber and fi.ccc in('O','Q','S'))>1")
                .apply(StringUtils.isNotEmpty(caseGrpTags),String.format("exists(select 1 from fi_creditcase_grp_tag fcgt where fcgt.creditcaseid = t.creditcaseid and fcgt.grp_tag in ('%s'))",caseGrpTags))
                .apply(StringUtils.isNotEmpty(customerGrpTags),String.format("exists(select 1 from com_customer_grp_tag ccgt where ccgt.customernumber = t.customernumber and ccgt.grp_tag in ('%s'))",caseGrpTags))
                .apply(StringUtils.isNotEmpty(paras.getEntpName()),String.format("exists(select 1 from con_company_info cci where cci.com_user_id=t.customernumber and cci.entp_name like '%s'))",paras.getEntpName()))
                .gt(ObjectUtils.isNotNull(paras.getLastCaseId()),FiCreditcases::getCreditcaseid,paras.getLastCaseId())
                .in(ObjectUtils.isNotNull(paras.getAssistant()),"fca.assistant",paras.getAssistant())
                .apply(ObjectUtils.isNotNull(paras.getAssiststatus()) && paras.getAssiststatus() == 1,"fca.status = 1")
                .apply(ObjectUtils.isNotNull(paras.getAssiststatus()) && paras.getAssiststatus() == 0,"fca.status is null")

                .ge(StringUtils.isNotEmpty(paras.getAssiststartdt1()),"fca.startdt",paras.getAssiststartdt1())
                .le(StringUtils.isNotEmpty(paras.getAssiststartdt2()),"fca.startdt",paras.getAssiststartdt2())
                ;
    }

    // 排序字段转换
    private String getColumn(String column){
        if(JPubUtils.IsEmpty(column)) {
            return column;
        }
        switch (column){
            case "mthsDay":
                column="t.mths_day";
                break;
            case "overdueTotalamt":
                column="t.overdue_totalamt";
                break;
            case "prododudtotalPrin":
                column="cu.prododudtotal_prin";
                break;
            case "overdueDays":
                column="cu.overdue_days";
                break;
            case "creditcaseid":
                column = "t.creditcaseid";
                break;
        }
        return column;
    }

    //得到控制案件查看权限SQL
    private String getPuvViewSql(String $usrid)
    {
        //数据库尽量不要使用函数，否则慢,尤其TDSQL
        //.apply("fi_hasFiCaseView({0},t.creditcaseid,t.status,t.sendtodept,t.sendtousr)>0", $usrid)
        return String.format(" (t.sendtousr='%s' or fca.assistant='%s') and t.status in (2,9) ",$usrid,$usrid);
    }

    //得到案件是停催SQL
    private String getStopCallSql()
    {
        //数据库尽量不要使用函数，否则慢,尤其TDSQL
        //fi_hasStopCall(t.creditcaseid,'CASE')>0
        return "(t.ccc='S' or exists (SELECT 1 FROM con_customertag tag WHERE tag.custid=t.customernumber AND tag.id IN ('DEAD','PAYCMD')))";
    }

    //得到客户死亡和支付令SQL
    private String getNoDeadSql()
    {
        //数据库尽量不要使用函数，否则慢,尤其TDSQL
        return "SELECT 1 FROM con_customertag tag WHERE tag.custid=t.customernumber AND tag.id IN ('DEAD','PAYCMD')";
    }

    @Override
    public IPage<CrdCaseResVo> crdCaseList(JSysUser user, CrdCaseReqVo paras,Boolean isEncryption)
    {
        String $usrid=user.getUsrid();
        String phnstar= "1";
        String idstar= "1";
        String cardstar= "1";
        String address= "1";
        if(isEncryption){
            phnstar= JUsrPerm.getPermission(user,JUsrPerm.DISSTAR_PHN);
            idstar= JUsrPerm.getPermission(user,JUsrPerm.DISSTAR_IDNO);
            address= JUsrPerm.getPermission(user,JUsrPerm.DISSTAR_ADDRESS);
            cardstar= JUsrPerm.getPermission(user,JUsrPerm.DISSTAR_CARD);
        }
        //boolean iscock=JPubUtils.hasValue(paras.getProdtype(),"3");
        MPJLambdaWrapper<FiCreditcases> quyWrapper=getQueryWrapper(user,paras);
        IPage<CrdCaseResVo> caseResVoIPage = selectJoinListPage(new Page<>(paras.getPageNum(), paras.getPageSize()),
                CrdCaseResVo.class,
                quyWrapper
                            .selectAs("tk.result", CrdCaseResVo::getLastccresult)
                            .select(FiCreditcases::getCreditcaseid,
                                FiCreditcases::getSerialNumber,
                                FiCreditcases::getBatchid,
                                FiCreditcases::getStatus,

                                FiCreditcases::getCcc,

                                FiCreditcases::getCreditAcct,
                                FiCreditcases::getProdtype,
                                FiCreditcases::getIndt,
                                FiCreditcases::getOverdueTotalamt,
                                FiCreditcases::getMthsDay,
                                FiCreditcases::getOverdueStage,

                                FiCreditcases::getDistdt,
                                FiCreditcases::getDistusr,
                                FiCreditcases::getDistbatchid,
                                FiCreditcases::getClosedt,

                                FiCreditcases::getSendtodept,
                                FiCreditcases::getSendtousr,
                                FiCreditcases::getRepay,
                                FiCreditcases::getAppointdt,
                                FiCreditcases::getLastccdt,
                                FiCreditcases::getBindenddt,
                                FiCreditcases::getStopautodistenddt,


                                FiCreditcases::getAssigndt,
                                FiCreditcases::getAssignusr,
                                FiCreditcases::getAssignid,
                                FiCreditcases::getRecycledt,
                                FiCreditcases::getRecycleusr,
                                FiCreditcases::getAmountnotdue,
                                FiCreditcases::getUnpaidprincipal,
                                FiCreditcases::getUnpayableinterest,
                                //FiCreditcases::getProductName,

                                FiCreditcases::getCpTotalamt,
                                FiCreditcases::getTotalRepaidAmount,
                                FiCreditcases::getWaitAmount,
                                FiCreditcases::getRepaidAmount,
                                FiCreditcases::getRemindRepayPrincipal,
                                FiCreditcases::getCpOverdueDays,
                                FiCreditcases::getCpStartTime,
                                FiCreditcases::getCpEndTime,
                                FiCreditcases::getStatusdt,
                                FiCreditcases::getDistributorid,
                                FiCreditcases::getProductId,
                                FiCreditcases::getDistributetype,
                                FiCreditcases::getAssigntype,
                                FiCreditcases::getRemindRepayDays,
                                FiCreditcases::getRecentlyRepTime

                        )
                        .selectAs("pr.product_name",CrdCaseResVo::getProductName)//商户名称
                        .selectAs("pr.merchant_name",CrdCaseResVo::getMerchantname)//商户名称
                        .select(ConCustomer::getCustomernumber,
                                ConCustomer::getCustname,
                                ConCustomer::getRisklevel,
                                ConCustomer::getCorpname,
                                ConCustomer::getPrododudtotalPrin,
                                ConCustomer::getProdmthsStage,
                                ConCustomer::getBirthday,
                                ConCustomer::getGender,
                                ConCustomer::getIdNation,
//                                ConCustomer::getCityAddress,
                                ConCustomer::getOverdueDays)


                        .selectAs(ConBlacklist::getType,CrdCaseResVo::getBlacklistType)
                        .selectAs("fca.startdt",CrdCaseResVo::getAssiststartdt)
                        .selectAs("fca.status",CrdCaseResVo::getAssiststatus)
                // .selectAs("GROUP_CONCAT(comp.entp_name SEPARATOR ',')",CrdCaseResVo::getEntpName)
                // .select(CreditAccount::getBillDate,CreditAccount::getMthsDay)
                // .select(CreditAccountBalance::getLowestRepayBal, CreditAccountBalance::getAcctDebtAll, CreditAccountBalance::getSettledBillAmt)
                .orderBy(JPubUtils.IsNotEmpty(paras.getColumn()),paras.getIsASC(),getColumn(paras.getColumn()))
        );
        if( caseResVoIPage == null || caseResVoIPage.getRecords().size() == 0)
            return caseResVoIPage;
        List<Integer> creditIds = new ArrayList<>();
        caseResVoIPage.convert(x->{
            //查询 标签信息
            x.setCustTag(fiCreditcasesTagMapper.getCasesTag(x.getCreditcaseid()));
            creditIds.add(x.getCreditcaseid());
            return x;
        });

        caseResVoIPage.convert(x->{
            List<SysTagsColor> custtags=conCustomertagService.selectJoinList(SysTagsColor.class,
                    new MPJLambdaWrapper<>(ConCustomertag.class)
                            .leftJoin(SysTags.class,SysTags::getId,ConCustomertag::getId)
                            .selectAs(ConCustomertag::getId,SysTagsColor::getId)
                            .selectAs(SysTags::getName,SysTagsColor::getDsc)
                            .selectAs(SysTags::getColor,SysTagsColor::getColor)
                            .eq(ConCustomertag::getCustid,x.getCustomernumber())
            );
            x.setConCustTag(custtags);
            return x;
        });

        List<CrdCaseResVo> CrdCaseResVoList = selectJoinList(
                CrdCaseResVo.class,
                new MPJLambdaWrapper<FiCreditcases>()
                    .innerJoin(ConCustomer.class, "cu",ConCustomer::getCustomernumber,FiCreditcases::getCustomernumber)
                    .leftJoin(SysUser.class, "su",SysUser::getUsrid,FiCreditcases::getSendtousr)
                    .leftJoin("fi_creditcase_assistant fca on fca.ficaseid = t.creditcaseid and fca.status = 1")

                    .selectAs(FiCreditcases::getCreditcaseid,CrdCaseResVo::getCreditcaseid)
                    .selectAs("fun_getnames('COLLECT.STATUS',t.status)", CrdCaseResVo::getStatusdsc)
                    .selectAs("fun_getnames('CUST.TYPE',cu.type)", CrdCaseResVo::getCusttypedsc)
                    .selectAs("(select g.grpname from sys_grp g where g.grpid=t.sendtodept)", CrdCaseResVo::getSendtodeptdsc)
                    .selectAs("fun_getnames('COLLECT.QUEUE',t.ccc)", CrdCaseResVo::getCccdsc)
                    .selectAs("fun_getnames('SORTING.QUEUE',t.disttag)", CrdCaseResVo::getDisttag)
                    .selectAs("fun_getnames('PRODUCT.TYPE',t.prodtype)", CrdCaseResVo::getProdtypedsc)
                    .selectAs("fi_hasStopCall(t.creditcaseid,'CASE')", CrdCaseResVo::getIsstopcase)
                    .selectAs(String.format("fi_starSecu('%s','%s',cu.custphone,'%s')", $usrid, JUsrPerm.DISSTAR_PHN, phnstar), CrdCaseResVo::getCustphone)
                    .selectAs(String.format("fi_starSecu('%s','%s',cu.city_address,'%s')", $usrid, JUsrPerm.DISSTAR_ADDRESS, address), CrdCaseResVo::getCityAddress)
                    .selectAs(String.format("fi_starSecu('%s','%s',cu.openacctcertno,'%s')", $usrid, JUsrPerm.DISSTAR_IDNO, idstar), CrdCaseResVo::getOpenacctcertno)
                    .selectAs("(select GROUP_CONCAT(fcgt.grp_tag ORDER BY null) from fi_creditcase_grp_tag fcgt where fcgt.creditcaseid=t.creditcaseid)",CrdCaseResVo::getCaseGrpTag)
                    .selectAs("(select GROUP_CONCAT(ccgt.grp_tag ORDER BY null) from con_customer_grp_tag ccgt where ccgt.customernumber=t.customernumber)",CrdCaseResVo::getCustGrpTag)
                    .selectAs("(select sum(loan_amt) from iou_loan_invoice_info where creditcaseid=t.creditcaseid)",CrdCaseResVo::getLoanAmt)
                    .selectAs(SysUser::getName, CrdCaseResVo::getSendtousr)
                    .selectAs("(select GROUP_CONCAT(cci.entp_name ORDER BY null) from con_company_info cci where cci.com_user_id=t.customernumber)",CrdCaseResVo::getEntpName)
                    .selectAs("(select distcrts.rule_step_name  from cfg_rule_task_steps distcrts where distcrts.rule_step_id=t.distributorid)",CrdCaseResVo::getDistributorName)
                    .selectAs("(select asscrts.rule_step_name  from cfg_rule_task_steps asscrts where asscrts.rule_step_id=t.assignid)",CrdCaseResVo::getAssignName)
                    .selectAs("(select sg.grpname from sys_grp sg where sg.type = 6 and sg.grpid = su.groups)",CrdCaseResVo::getTeamname)
                    .selectAs("fun_getusername(fca.assistant)",CrdCaseResVo::getAssistant)
                    .in(FiCreditcases::getCreditcaseid,creditIds)
                );
        if(CrdCaseResVoList==null){
            return caseResVoIPage;
        }

        Map<Integer, CrdCaseResVo> caseList = CrdCaseResVoList.stream().collect(Collectors.toMap(item -> item.getCreditcaseid(), item -> item));


        int i = 0;
        while(true)
        {
            CrdCaseResVo crdCaseResVo = caseList.get(caseResVoIPage.getRecords().get(i).getCreditcaseid());
            caseResVoIPage.getRecords().get(i).setStatusdsc(crdCaseResVo.getStatusdsc());
            caseResVoIPage.getRecords().get(i).setCusttypedsc(crdCaseResVo.getCusttypedsc());
            caseResVoIPage.getRecords().get(i).setSendtodeptdsc(crdCaseResVo.getSendtodeptdsc());
            caseResVoIPage.getRecords().get(i).setCccdsc(crdCaseResVo.getCccdsc());
            caseResVoIPage.getRecords().get(i).setDisttag(crdCaseResVo.getDisttag());
            caseResVoIPage.getRecords().get(i).setProdtypedsc(crdCaseResVo.getProdtypedsc());
            caseResVoIPage.getRecords().get(i).setIsstopcase(crdCaseResVo.getIsstopcase());
            caseResVoIPage.getRecords().get(i).setCustphone(crdCaseResVo.getCustphone());
            caseResVoIPage.getRecords().get(i).setOpenacctcertno(crdCaseResVo.getOpenacctcertno());
            caseResVoIPage.getRecords().get(i).setCaseGrpTag(crdCaseResVo.getCaseGrpTag());
            caseResVoIPage.getRecords().get(i).setCustGrpTag(crdCaseResVo.getCustGrpTag());
            caseResVoIPage.getRecords().get(i).setLoanAmt(crdCaseResVo.getLoanAmt());
            caseResVoIPage.getRecords().get(i).setEntpName(crdCaseResVo.getEntpName());
            caseResVoIPage.getRecords().get(i).setCityAddress(crdCaseResVo.getCityAddress());
            caseResVoIPage.getRecords().get(i).setTeamname(crdCaseResVo.getTeamname());
            caseResVoIPage.getRecords().get(i).setAssistant(crdCaseResVo.getAssistant());
            if( ++i ==  CrdCaseResVoList.size())
                break;
        }
        return caseResVoIPage;


        // .orderByDesc(FiCreditcases::getIndt) 取消排序，否则TDSQL非常慢
//        }
//        else{
//            return selectJoinListPage(new Page<>(paras.getPageNum(), paras.getPageSize()),
//                    CrdCaseResVo.class,
//                    quyWrapper.selectAs("fun_getnames('COLLECT.STATUS',t.status)", CrdCaseResVo::getStatusdsc)
//                    .selectAs("fun_getnames('CUST.TYPE',cu.type)", CrdCaseResVo::getCusttypedsc)
//                    .selectAs("(select g.grpname from sys_grp g where g.grpid=t.sendtodept)", CrdCaseResVo::getSendtodeptdsc)
//                    .selectAs("fun_getnames('COLLECT.QUEUE',t.ccc)", CrdCaseResVo::getCccdsc)
//                    .selectAs("fun_getnames('BUSINESS.TYPE',t.prodtype)", CrdCaseResVo::getProdtypedsc)
//                    .selectAs("cct.crdprin", CrdCaseResVo::getSettledBillAmt)
//                    .selectAs("cct.debttotal", CrdCaseResVo::getOdudPrin)
//                    .selectAs("fi_hasStopCall(t.creditcaseid,'CASE')", CrdCaseResVo::getIsstopcase)
//                    .selectAs("tk.result", CrdCaseResVo::getLastccresult)
//                    .selectAs(String.format("fi_starSecu('%s','%s',cu.custphone,'%s')", $usrid, JUsrPerm.DISSTAR_PHN, phnstar), CrdCaseResVo::getCustphone)
//                    .selectAs(String.format("fi_starSecu('%s','%s',cu.openacctcertno,'%s')", $usrid, JUsrPerm.DISSTAR_IDNO, idstar), CrdCaseResVo::getOpenacctcertno)
//                    .select(FiCreditcases::getCreditcaseid, FiCreditcases::getStatus, FiCreditcases::getCcc, FiCreditcases::getCreditAcct, FiCreditcases::getProdtype,
//                            FiCreditcases::getIndt, FiCreditcases::getDistdt, FiCreditcases::getSendtodept, FiCreditcases::getSendtousr, FiCreditcases::getRepay,
//                            FiCreditcases::getAppointdt, FiCreditcases::getLastccdt, FiCreditcases::getBindenddt, FiCreditcases::getStopautodistenddt,
//                            FiCreditcases::getDistusr,FiCreditcases::getAssigndt,FiCreditcases::getAssignusr)
//                    .select(ConCustomer::getCustomernumber, ConCustomer::getCustname, ConCustomer::getRisklevel, ConCustomer::getCorpname)
//                    .orderByDesc(FiCreditcases::getIndt)
//            );
//        }
    }

    @Override
    public CrdCaseStatisticsResVo crdCaseListStatistics(JSysUser user, CrdCaseReqVo paras) {
        MPJLambdaWrapper<FiCreditcases> quyWrapper=getQueryWrapper(user,paras);
        CrdCaseStatisticsResVo caseStatisticsResVo = selectJoinOne(CrdCaseStatisticsResVo.class,
                quyWrapper.selectSum(FiCreditcases::getOverdueTotalamt, CrdCaseStatisticsResVo::getOverdueTotalamt)
                        .selectSum(FiCreditcases::getCpTotalamt, CrdCaseStatisticsResVo::getCpTotalamt)
        );
//        if(caseStatisticsResVoList==null){
//            return new CrdCaseStatisticsResVo();
//        }

//        CrdCaseStatisticsResVo crdCaseStatisticsResVo=new CrdCaseStatisticsResVo();

//        crdCaseStatisticsResVo.setCpTotalamt(
//                caseStatisticsResVoList.stream().map(CrdCaseStatisticsResVo::getCpTotalamt).filter(Objects::nonNull).reduce(BigDecimal.ZERO, BigDecimal::add)
//        );
//        crdCaseStatisticsResVo.setOverdueTotalamt(
//                caseStatisticsResVoList.stream().map(CrdCaseStatisticsResVo::getOverdueTotalamt).filter(Objects::nonNull).reduce(BigDecimal.ZERO, BigDecimal::add)
//        );
//
//        if(crdCaseStatisticsResVo.getCpTotalamt()!=null){
//            crdCaseStatisticsResVo.setCpTotalamt(crdCaseStatisticsResVo.getCpTotalamt().setScale(2, RoundingMode.HALF_UP));
//        }
//        if(crdCaseStatisticsResVo.getOverdueTotalamt()!=null){
//            crdCaseStatisticsResVo.setOverdueTotalamt(crdCaseStatisticsResVo.getOverdueTotalamt().setScale(2, RoundingMode.HALF_UP));
//        }
        return caseStatisticsResVo;
    }

    @Override
    public CrdQuySumVo getQueryDebtSum(JSysUser user, CrdCaseReqVo paras)
    {
        boolean iscock=JPubUtils.hasValue(paras.getProdtype(),"3");
        MPJLambdaWrapper<FiCreditcases> quyWrapper=getQueryWrapper(user,paras);
        //if(!iscock) {
        return selectJoinOne(CrdQuySumVo.class,
                quyWrapper.selectSum(FiCreditcases::getOverdueTotalamt, CrdQuySumVo::getSettledBillAmtSum)
        );
//        }
//        else{
//            return selectJoinOne(CrdQuySumVo.class,
//                    quyWrapper.selectSum(CreditCockcollect::getCrdprin, CrdQuySumVo::getSettledBillAmtSum)
//            );
//        }
    }

    /**
     * 20250513 增加参数 disthistid：分案闽西的histid。
     * @param user
     * @param creditcaseid
     * @param popup
     * @return
     */
    @Override
    public CrdCaseInfoVo getCrdCaseInfo(JSysUser user, Integer creditcaseid, String popup, String disthistid ){//单个查询也需要控制权限，防止页面输入creditcaseid直接打开
        // 2024-06-27 edit 当前账户余额 由总欠款字段改成：已出账单欠款+未出账单欠款
        String $usrid=user.getUsrid();
        String idstar= JUsrPerm.getPermission(user,JUsrPerm.DISSTAR_IDNO);
        String cardstar= JUsrPerm.getPermission(user,JUsrPerm.DISSTAR_CARD);
        String addressstar = JUsrPerm.getPermission(user,JUsrPerm.DISSTAR_ADDRESS);
        //2024-07-24
        boolean viewAllCase=JUsrPerm.viewAllFiCase(user);

        // 只在弹屏的时候使用 放开查询条件
        if(popup.equals("view")){
            popup=JUsrPerm.VIEW_ALLFICASE;
            viewAllCase=true;
        }
        boolean viewGrpCase = false;
        if(!viewAllCase && user.getMgrgrplist().size()>0)
            viewGrpCase = true;

        CrdCaseInfoVo resVo=selectJoinOne(
                CrdCaseInfoVo .class,
                new MPJLambdaWrapper<FiCreditcases>()
                        .selectAs("fun_getnames('COLLECT.STATUS',t.status)",CrdCaseInfoVo::getStatusdsc)
                        .selectAs("fun_getnames('CUST.TYPE',cu.type)",CrdCaseInfoVo::getCusttypedsc)
                        .selectAs("(select g.grpname from sys_grp g where g.grpid=t.sendtodept)",CrdCaseInfoVo::getSendtodeptdsc)
//                        .selectAs("fun_getnames('COLLECT.ODUE',cda.mths_odue)",CrdCaseInfoVo::getMthsOdue)
                        .selectAs("fun_getnames('COLLECT.QUEUE',t.ccc)", CrdCaseResVo::getCccdsc)
                        .selectAs("fun_getnames('SORTING.QUEUE',t.disttag)", CrdCaseResVo::getDisttag)
                        .selectAs("fun_getnames('PRODUCT.TYPE',t.prodtype)",CrdCaseInfoVo::getProdtypedsc)
                        .selectAs("fun_getnames('MARRIAGE.STATUS',cu.marrgstatus)",CrdCaseInfoVo::getMarrgstatus)
                        .selectAs("fun_getnames('CON.SEX',cu.gender)",CrdCaseInfoVo::getGender)
                        .selectAs("fun_getnames('CON.EDU',cu.heightedudegr)",CrdCaseInfoVo::getHeightedudegr)
                        .selectAs("fun_getnames('CON.PSTN',cu.pstn)",CrdCaseInfoVo::getPstn)
                        .selectAs("fun_getnames('CON.CERTTYPE',cu.openacctcerttype)", CrdCaseInfoVo::getOpenacctcerttype)
                        .selectAs("(select name from sys_user where usrid = t.sendtousr)",CrdCaseInfoVo::getSendtousr)
//                        .selectAs("fun_getnames('CREDIT.ACCT.STATUS',cda.acct_status)",CrdCaseInfoVo::getAcctStatus)
//                        .selectAs("cdb.age1_age6",CrdCaseInfoVo::getOdudPrin)
                        .selectAs("fi_hasHoldCard(t.credit_acct)",CrdCaseInfoVo::getIsholdCard)
                        .selectAs("fi_hasStopCall(t.creditcaseid,'CASE')", CrdCaseInfoVo::getIsstopcase)
//                        .selectAs("cdb.mddt", CrdCaseInfoVo::getAccBalLastUpTime)
//                        .selectAs("fun_getnames('CREDIT.ACCT.CY',cda.currency)",CrdCaseInfoVo::getCurrency)
//                        .selectAs("fun_getnames('CREDIT.ACCT.FLEVEL',cda.acct_flevel)",CrdCaseInfoVo::getAcctFlevel)
//                        .selectAs("(case when cdb.settled_bill_amt is null then 0.0 else cdb.settled_bill_amt end+case when cdb.unsettled_bill_amt is null then 0.0 else cdb.unsettled_bill_amt end)",CrdCaseInfoVo::getAcctDebtAmt)
                        .selectAs(String.format("fi_starSecu('%s','%s',cu.openacctcertno,'%s')",$usrid,JUsrPerm.DISSTAR_IDNO,idstar),CrdCaseInfoVo::getOpenacctcertno)
                        .selectAs(String.format("fi_starSecu('%s','%s',cu.debitcardno,'%s')",$usrid,JUsrPerm.DISSTAR_CARD,cardstar),CrdCaseInfoVo::getDebitcardno)
//                        .selectAs(String.format("fi_starSecu('%s','%s',cda.debit_card_num,'%s')",$usrid,JUsrPerm.DISSTAR_CARD,cardstar),CrdCaseInfoVo::getDebitCardNum)
//                        .selectAs("fc_hasFiAmort(t.creditcaseid)", CrdCaseInfoVo::getFiamortcount)

                        .innerJoin(ConCustomer.class,"cu",ConCustomer::getCustomernumber,FiCreditcases::getCustomernumber)
                        .leftJoin(String.format(" fi_creditcases_hist fch on fch.creditcaseid = t.creditcaseid and fch.histid=%s",StringUtils.isNotEmpty(disthistid)?disthistid:"0"))
                        .leftJoin("fi_creditcase_assistant fca on fca.ficaseid = t.creditcaseid and fca.status = 1")
                        .in(viewGrpCase && StringUtils.isEmpty(disthistid), FiCreditcases::getSendtodept, user.getMgrgrplist())
                        .apply(!viewAllCase && !viewGrpCase && StringUtils.isEmpty(disthistid),getPuvViewSql($usrid))
                        .in(viewGrpCase && StringUtils.isNotEmpty(disthistid), "fch.sendtodept", user.getMgrgrplist())
                        .eq(!viewAllCase && !viewGrpCase && StringUtils.isNotEmpty(disthistid),"fch.sendtodept",user.getGroups())

//                        .leftJoin(FiTask.class, "tk", FiTask::getTasknumber, FiCreditcases::getLastcctaskid)
//                        .leftJoin(CreditAccount.class,"cda",CreditAccount::getAcct,FiCreditcases::getCreditAcct)
//                        .leftJoin(CreditAccountBalance.class,"cdb",CreditAccountBalance::getAcct,FiCreditcases::getCreditAcct)
                        //.leftJoin(IouProduct.class, "pr", IouProduct::getProductId, FiCreditcases::getProductId)
//                        .leftJoin("iou_product pr on pr.product_id = t.product_id and ifnull(pr.merchant_id,'') = ifnull(t.merchant_id,'')")
//                        .leftJoin(ConCustomerCompanyInfo.class, "customerCom", ConCustomerCompanyInfo::getUserId, ConCustomer::getCustomernumber)
//                        .leftJoin(ConCompanyInfo.class, "company", ConCompanyInfo::getComUserId, ConCustomer::getCustomernumber)
//                        .leftJoin(ConCycswbz.class, "cycswbz", ConCycswbz::getEntpCertno, ConCompanyInfo::getEntpCertNo)
//                        .leftJoin(ConBlacklist.class, "cbl", ConBlacklist::getCustomernumber, ConCustomer::getCustomernumber)
//                        .leftJoin("(SELECT fch.* FROM fi_creditcases_hist fch  INNER JOIN( SELECT max(h.histid) AS histid FROM fi_creditcases_hist h WHERE h.actiontype=1 GROUP BY h.creditcaseid ORDER BY h.histid DESC )fchs ON fch.histid=fchs.histid) h ON t.creditcaseid=h.creditcaseid ")
//                        .leftJoin("(SELECT fch.* FROM fi_creditcases_hist fch  INNER JOIN( SELECT max(h.histid) AS histid FROM fi_creditcases_hist h WHERE h.actiontype=3 GROUP BY h.creditcaseid ORDER BY h.histid DESC )fchs ON fch.histid=fchs.histid) h1 ON t.creditcaseid=h1.creditcaseid")
//                        .leftJoin("(SELECT fch.* FROM fi_creditcases_hist fch  INNER JOIN( SELECT max(h.histid) AS histid FROM fi_creditcases_hist h WHERE h.actiontype=4 GROUP BY h.creditcaseid ORDER BY h.histid DESC )fchs ON fch.histid=fchs.histid) h2 ON t.creditcaseid=h2.creditcaseid")
////                        .leftJoin("(SELECT originnumber,result,appointdt  FROM fi_task GROUP BY originnumber,result) task ON t.creditcaseid=task.originnumber AND task.result='APPOINT'")
//                        .leftJoin("(SELECT task.originnumber,task.crdt FROM fi_task task INNER JOIN (SELECT max(tasknumber) AS tasknumber FROM fi_task GROUP BY originnumber)tsi ON task.tasknumber=tsi.tasknumber ) task2 ON t.creditcaseid=task2.originnumber ")
//                        .leftJoin("iou_product ioup on ioup.product_id = t.product_id and ifnull(ioup.merchant_id,'') = ifnull(t.merchant_id,'')")

//                        .leftJoin(IouProduct.class, "ioup",
//                                on->(on.eq(IouProduct::getProductId, FiCreditcases::getProductId)
//                                        .eq(IouProduct::getMerchantId,FiCreditcases::getMerchantId)
////                                        .eq(IouProduct::getProductType,FiCreditcases::getProdtype)
//                        ))
                        .selectAll(FiCreditcases.class)
//                         .select(IouProduct::getMerchantName)
//                        .selectAs("task.appointdt",CrdCaseInfoVo::getNextContactTime)
                        .selectAs(FiCreditcases::getAppointdt,CrdCaseInfoVo::getNextContactTime)
                     //   .selectAs("task2.crdt",CrdCaseInfoVo::getLasttaskdt)
                        .selectAs("DATEDIFF(CURDATE(), t.indt)+1",CrdCaseInfoVo::getDaysIntoCollection)
                     //   .selectAs("ioup.merchant_name",CrdCaseInfoVo::getMerchantname)//商户名称
                     //   .selectAs("ioup.source",CrdCaseInfoVo::getProductSource)//商户名称
                     //   .selectAs("h.pre_repay_principal + h.unpaidprincipal",CrdCaseInfoVo::getCpRemainingPrincipal)
                     //   .selectAs("h1.mths_day",CrdCaseInfoVo::getAfterRecoveryOverdueDays)
                     //   .selectAs("h1.overdue_totalamt",CrdCaseInfoVo::getAfterRecoveryOverdueAmount)
                     //   .selectAs("h1.pre_repay_principal",CrdCaseInfoVo::getAfterRecoveryOverduePrincipal)
                     //   .selectAs("h1.unpaidprincipal",CrdCaseInfoVo::getAfterRecoveryRemainingPrincipal)
                     //   .selectAs("h1.wait_amount",CrdCaseInfoVo::getRecycleWaitAmount)
                     //   .selectAs("h2.total_repaid_amount",CrdCaseInfoVo::getOutRemainingPrincipal)
                     //   .selectAs(ConBlacklist::getType,CrdCaseInfoVo::getBlacklistType)
                        .select(ConCustomer::getCustomernumber,
                                ConCustomer::getCustname,
                                ConCustomer::getRisklevel,
                                ConCustomer::getDepartment,
                                ConCustomer::getAnnualincome,
                                ConCustomer::getCustacctorgno,
                                ConCustomer::getCorpname,
                                ConCustomer::getCustenname,
                                ConCustomer::getCustemail,
                                ConCustomer::getIsbankemply,
                                ConCustomer::getBirthday,
                                ConCustomer::getOverdueProductsC,
                                ConCustomer::getProdmthsStage,
                                ConCustomer::getOverdueDays,
                                ConCustomer::getSalary)
                     //   .selectAs("(select sum(ifnull(cp_totalamt,0)) from fi_creditcases where customernumber = t.customernumber)", CrdCaseInfoVo::getCuCpTotalamt)
//                        .selectAs("(select sum(fc2.remind_repay_principal) from \n" +
//                                "(\n" +
//                                "select customernumber, ifnull(min( remind_repay_days),999999) remind_repay_days\n" +
//                                "\n" +
//                                "from fi_creditcases \n" +
//                                "group by customernumber\n" +
//                                ") tt \n" +
//                                "join fi_creditcases fc2 on fc2.customernumber = tt.customernumber \n" +
//                                "and fc2.remind_repay_days = tt.remind_repay_days\n" +
//                                "where fc2.customernumber = t.customernumber)", CrdCaseInfoVo::getCuRemindRepayPrincipal)
//
                        .selectAs(ConCustomer::getTotalRepaidAmount,CrdCaseInfoVo::getCuTotalRepaidAmount)
                        .selectAs(ConCustomer::getOutstandingInterest,CrdCaseInfoVo::getCuOutstandingInterest)
                        .selectAs(ConCustomer::getRemainingPrincipal,CrdCaseInfoVo::getCuRemainingPrincipal)
                        .selectAs(ConCustomer::getPrododudtotalPrin,CrdCaseInfoVo::getCuPrododudtotalPrin)

                        .selectAs(String.format("fi_starSecu('%s','%s',cu.id_no_address,'%s')",$usrid,JUsrPerm.DISSTAR_ADDRESS,addressstar),CrdCaseInfoVo::getIdNoAddress)
                        .selectAs(String.format("fi_starSecu('%s','%s',cu.city_address,'%s')",$usrid,JUsrPerm.DISSTAR_ADDRESS,addressstar),CrdCaseInfoVo::getCityAddress)
                        .selectAs(FiCreditcases::getSendtousr, CrdCaseInfoVo::getSendtousrid)
                        .selectAs("fca.assistant",CrdCaseInfoVo::getAssistantid)
                        .selectAs("fca.status",CrdCaseInfoVo::getAssiststatus)
                        .selectAs("fca.startdt",CrdCaseInfoVo::getAssiststartdt)
                        .selectAs("fun_getusername(fca.assistant)",CrdCaseInfoVo::getAssistantname)

//                        .selectAs("(select GROUP_CONCAT(distinct ili.repay_day ORDER BY ili.repay_day) from iou_loan_invoice_info ili where ili.creditcaseid=t.creditcaseid and ili.loan_invoice_status in ('1','2'))",CrdCaseInfoVo::getRepayDate)
                        .eq("t.creditcaseid",creditcaseid)
        );
//        List<ConCustomertag> custtags=conCustomertagMapper.selectJoinList(
//                ConCustomertag.class,
//                new MPJLambdaWrapper<ConCustomertag>()
//                        .leftJoin(FiCreditcases.class,"fc",FiCreditcases::getCustomernumber,ConCustomertag::getCustid)
//                        .selectAll(ConCustomertag.class)
//                        .eq("fc.creditcaseid",creditcaseid)
//        );

        List<SysTagsColor> custtags=conCustomertagService.selectJoinList(SysTagsColor.class,
                new MPJLambdaWrapper<>(ConCustomertag.class)
                        .leftJoin(FiCreditcases.class,"fc",FiCreditcases::getCustomernumber,ConCustomertag::getCustid)
                        .leftJoin(SysTags.class,SysTags::getId,ConCustomertag::getId)
                        .selectAs(ConCustomertag::getId,SysTagsColor::getId)
                        .selectAs(SysTags::getName,SysTagsColor::getDsc)
                        .selectAs(SysTags::getColor,SysTagsColor::getColor)
                        .eq("fc.creditcaseid",creditcaseid)
        );
        resVo.setCusttags(custtags);
        resVo.setCustTag(fiCreditcasesTagMapper.getCasesTag(resVo.getCreditcaseid()));


        //获取产品可用API接口
        resVo.setApiIdList(iouPorductApiService.list(new MPJLambdaWrapper<>(IouPorductApi.class)
                .eq(IouPorductApi::getProductId, resVo.getProductId())
                .eq(IouPorductApi::getMerchantId, resVo.getMerchantId())
        ).stream().map(IouPorductApi::getApiId).collect(Collectors.toList()));

        return resVo;
    }
    @Override
    public CrdCaseExtraInfo getCrdCaseExtraInfo(Integer creditcaseid)
    {
        CrdCaseExtraInfo crdCaseExtraInfo = selectJoinOne(
                CrdCaseExtraInfo.class,
                new MPJLambdaWrapper<FiCreditcases>()
                        .leftJoin("fi_creditcases_hist_link fchl on fchl.creditcaseid=t.creditcaseid")
//                        .leftJoin("(SELECT fch.* FROM fi_creditcases_hist fch  INNER JOIN( SELECT max(h.histid) AS histid FROM fi_creditcases_hist h WHERE h.actiontype=1 GROUP BY h.creditcaseid ORDER BY h.histid DESC )fchs ON fch.histid=fchs.histid) h ON t.creditcaseid=h.creditcaseid ")
//                        .leftJoin("(SELECT fch.* FROM fi_creditcases_hist fch  INNER JOIN( SELECT max(h.histid) AS histid FROM fi_creditcases_hist h WHERE h.actiontype=3 GROUP BY h.creditcaseid ORDER BY h.histid DESC )fchs ON fch.histid=fchs.histid) h1 ON t.creditcaseid=h1.creditcaseid")
//                        .leftJoin("(SELECT fch.* FROM fi_creditcases_hist fch  INNER JOIN( SELECT max(h.histid) AS histid FROM fi_creditcases_hist h WHERE h.actiontype=4 GROUP BY h.creditcaseid ORDER BY h.histid DESC )fchs ON fch.histid=fchs.histid) h2 ON t.creditcaseid=h2.creditcaseid")
//                        .leftJoin("(SELECT task.originnumber,task.crdt FROM fi_task task INNER JOIN (SELECT max(tasknumber) AS tasknumber FROM fi_task GROUP BY originnumber)tsi ON task.tasknumber=tsi.tasknumber ) task2 ON t.creditcaseid=task2.originnumber ")
                        .leftJoin("fi_creditcases_hist h  ON h.histid=fchl.disthistid")
                        .leftJoin("fi_creditcases_hist h1 ON h1.histid=fchl.recyclehistid")
                        .leftJoin("fi_creditcases_hist h2 ON h2.histid=fchl.closehistid")
                        .leftJoin("fi_task task2 on task2.tasknumber=fchl.lasttasknumber")
                        .leftJoin("iou_product ioup on ioup.product_id = t.product_id and ifnull(ioup.merchant_id,'') = ifnull(t.merchant_id,'')")
                        .leftJoin(ConBlacklist.class, "cbl", ConBlacklist::getCustomernumber, FiCreditcases::getCustomernumber)
                        .leftJoin(" ( select user_id, product_id, sum(total_amount) total_amount, " +
                                "sum(using_amount) used_amount from pl_personal_limit_detail group by user_id, product_id) pld  " +
                                " on pld.user_id = t.customernumber and pld.product_id = ifnull(ioup.parent_id, ioup.product_id)")
                        .leftJoin("fi_creditcase_assistant fca on fca.ficaseid = t.creditcaseid and fca.status = 1")
                        .selectAs(FiCreditcases::getCreditcaseid,CrdCaseExtraInfo::getCreditcaseid)
                        .selectAs("fc_hasFiAmort(t.creditcaseid)", CrdCaseExtraInfo::getFiamortcount)
                        .selectAs("task2.crdt",CrdCaseExtraInfo::getLasttaskdt)

                        .selectAs("ioup.merchant_name",CrdCaseExtraInfo::getMerchantname)//商户名称
                        .selectAs("ioup.source",CrdCaseExtraInfo::getProductSource)//商户名称
                        .selectAs("h.pre_repay_principal + h.unpaidprincipal",CrdCaseExtraInfo::getCpRemainingPrincipal)
                        .selectAs("h1.mths_day",CrdCaseExtraInfo::getAfterRecoveryOverdueDays)
                        .selectAs("h1.overdue_totalamt",CrdCaseExtraInfo::getAfterRecoveryOverdueAmount)
                        .selectAs("h1.pre_repay_principal",CrdCaseExtraInfo::getAfterRecoveryOverduePrincipal)
                        .selectAs("h1.unpaidprincipal",CrdCaseExtraInfo::getAfterRecoveryRemainingPrincipal)
                        .selectAs("h1.wait_amount",CrdCaseExtraInfo::getRecycleWaitAmount)
                        .selectAs("h2.total_repaid_amount",CrdCaseExtraInfo::getOutRemainingPrincipal)
                        .selectAs("(select sum(ifnull(cp_totalamt,0)) from fi_creditcases where customernumber = t.customernumber)", CrdCaseExtraInfo::getCuCpTotalamt)
                        .selectAs("(select sum(fc2.remind_repay_principal) from \n" +
                                "(\n" +
                                "select customernumber, ifnull(min( remind_repay_days),999999) remind_repay_days\n" +
                                "\n" +
                                "from fi_creditcases \n" +
                                "group by customernumber\n" +
                                ") tt \n" +
                                "join fi_creditcases fc2 on fc2.customernumber = tt.customernumber \n" +
                                "and fc2.remind_repay_days = tt.remind_repay_days\n" +
                                "where fc2.customernumber = t.customernumber)", CrdCaseExtraInfo::getCuRemindRepayPrincipal)

                        .selectAs(ConBlacklist::getType,CrdCaseExtraInfo::getBlacklistType)
                        .selectAs("(select GROUP_CONCAT(distinct ili.repay_day ORDER BY ili.repay_day) from iou_loan_invoice_info ili where ili.creditcaseid=t.creditcaseid and ili.loan_invoice_status in ('1','2'))",CrdCaseExtraInfo::getRepayDate)
                        .selectAs("t.product_id",CrdCaseExtraInfo::getProductId)
                        .selectAs("ifnull(pld.total_amount,0)",CrdCaseExtraInfo::getCreditAmount)
                        .selectAs("ifnull(pld.used_amount,0)",CrdCaseExtraInfo::getUsedAmount)
                        .eq("t.creditcaseid",creditcaseid)
        );
        //获取授信额度
        if(crdCaseExtraInfo.getProductId().equals("HLD009001")
                && crdCaseExtraInfo.getCreditAmount().compareTo(BigDecimal.ZERO) == 0
        ) {
            log.info("Product id is {}, creditAmount: {},get old product HLDJQH01 credit amount", crdCaseExtraInfo.getProductId(),crdCaseExtraInfo.getCreditAmount());
            CrdCaseExtraInfo crdCaseExtraInfo2 = selectJoinOne(
                    CrdCaseExtraInfo.class,
                    new MPJLambdaWrapper<FiCreditcases>()
                            .leftJoin(" ( select user_id, product_id, sum(total_amount) total_amount, " +
                                    "sum(using_amount) used_amount from pl_personal_limit_detail group by user_id, product_id) pld  " +
                                    " on pld.user_id = t.customernumber and pld.product_id = 'HLDJQH01'")
                            .eq("t.creditcaseid",creditcaseid)
                            .selectAs("ifnull(pld.total_amount,0)",CrdCaseExtraInfo::getCreditAmount)
                            .selectAs("ifnull(pld.used_amount,0)",CrdCaseExtraInfo::getUsedAmount)

            );
            if(crdCaseExtraInfo2 != null ){
                crdCaseExtraInfo.setCreditAmount(crdCaseExtraInfo2.getCreditAmount());
                crdCaseExtraInfo.setUsedAmount(crdCaseExtraInfo2.getUsedAmount());
            }
        }
        return crdCaseExtraInfo;
    }

    @Override
    public List<ConCompanyResVo> companyList(JSysUser user, String customernumber) {

        return conCompanyInfoService.selectJoinList(ConCompanyResVo.class,
                new MPJLambdaWrapper<>(ConCompanyInfo.class)
                        .selectAs(ConCompanyInfo::getEntpName,ConCompanyResVo::getEntpName)
                        .selectAs(ConCycswbz::getLegalPersonName,ConCompanyResVo::getLegalPersonName)
                        .selectAs(ConCompanyInfo::getBusComNumber,ConCompanyResVo::getBusComNumber)
                        .selectAs(ConCompanyInfo::getRegisteredCapital,ConCompanyResVo::getRegisteredCapital)
                        .selectAs(ConCompanyInfo::getBusiStartDate,ConCompanyResVo::getBusiStartDate)
                        .selectAs(ConCompanyInfo::getBusiEndDate,ConCompanyResVo::getBusiEndDate)
                        .selectAs(ConCycswbz::getLegalPersonIdNumber,ConCompanyResVo::getLegalPersonIdNumber)
                        .selectAs(ConCompanyInfo::getEntpCertNo,ConCompanyResVo::getEntpCertNo)
                        .leftJoin(ConCycswbz.class, "cycswbz", ConCycswbz::getEntpCertno, ConCompanyInfo::getEntpCertNo)
                        .eq(ConCompanyInfo::getComUserId,customernumber)
        );
    }

    @Override
    public IPage<FiAppointResVo> fiAppointCaseList(String $usrid,FiAppointReqVo paras)
    {//本人今日预约案件LIST
        SimpleDateFormat formatter=new SimpleDateFormat("yyyy-MM-dd");
        //String today=formatter.format(new Date())+" 00:00:00";
        String today=formatter.format(new Date()); //2024-08-29 不能加时间，否则就变成当前时间了
        return selectJoinListPage(new Page<>(paras.getPageNum(), paras.getPageSize()),
                FiAppointResVo.class,
                new MPJLambdaWrapper<FiCreditcases>()
                        .innerJoin(ConCustomer.class, ConCustomer::getCustomernumber, FiCreditcases::getCustomernumber)
                        .leftJoin(FiPtp.class, "fptp", FiPtp::getCreditcaseid, FiCreditcases::getCreditcaseid)
                        .selectAs("t.creditcaseid",FiAppointResVo::getFicaseid)
                        .select(
                                FiCreditcases::getProductName,
                                FiCreditcases::getAppointdt,
                                FiCreditcases::getLastccdt,
                                FiCreditcases::getAssigndt,
                                FiCreditcases::getSendtousr,
                                FiCreditcases::getOverdueStage,
                                FiCreditcases::getMthsDay,
                                FiCreditcases::getOverdueTotalamt)
                        .selectAs(ConCustomer::getCustname,FiAppointResVo::getCustname)
                        .selectAs(FiPtp::getPtpDate,FiAppointResVo::getPtpDate)
                        .selectAs(FiPtp::getPtpAmt,FiAppointResVo::getPtpAmt)
                        .selectAs(FiPtp::getStatus,FiAppointResVo::getPtpStauts)
                        .selectAs("fun_getnames('FI.REPAY.WILL',fptp.ptp_type)",FiAppointResVo::getPtpTypeDesc)
                        .isNotNull(FiCreditcases::getSendtousr)
                        .eq(FiCreditcases::getStatus,EnumFiCaseStatus.Assign.value)
                        .eq(FiCreditcases::getSendtousr,$usrid)
                        .ge(FiCreditcases::getAppointdt,JPubUtils.StringToDate(today))
                        .orderByAsc(FiCreditcases::getAppointdt)
        );
    }

    @Override
    public List<GrpCaseCountVo> grpCaseCountList(GrpListReqVo paras)
    {
        return selectJoinList(GrpCaseCountVo.class,
                new MPJLambdaWrapper<FiCreditcases>()
                        .selectAs("(select g.grpname from sys_grp g where g.grpid=t.sendtodept)",GrpCaseCountVo::getGroupname)
                        .selectAs("count(1)",GrpCaseCountVo::getAllcount)
                        .selectAs("sum(case when f.ccc in('Q','S') then 1 else 0 end)",GrpCaseCountVo::getQcount)
                        .selectAs("sum(case when f.ccc='R' then 1 else 0 end)",GrpCaseCountVo::getRcount)
                        .selectAs("sum(case when f.ccc='S' then 1 else 0 end)",GrpCaseCountVo::getScount)
                        .isNotNull(FiCreditcases::getSendtodept)
                        .isNotNull(FiCreditcases::getCcc)
                        .in(FiCreditcases::getCcc,"Q","R","S","F")
                        .in(JPubUtils.IsNotEmpty(paras.getGrplist()),FiCreditcases::getSendtodept,paras.getGrplist())
                        .groupBy(FiCreditcases::getSendtodept)
        );
    }

    /**
     * 预分配，并返回预分配的结果。
     * @param caseList
     * @param distReqVo
     * @return
     */
      private List<DistResVo> preDistCase(List<Integer> caseList, DistReqVo distReqVo )
      {
          List<DistResVo> distResVoList=new LinkedList<>();
          //String sql = " fc.creditcaseid in (" + caseList.stream().map(Object::toString).collect(Collectors.joining()) + ")";
          //List<DistCreditcaseVo> amtList=  baseMapper.getDistObjectList(0, caseList.size(), sql, distReqVo.getAmountType());
          //初始化各个委案分配的案件数量。
          List<DistCreditcaseVo> amtList= caseSortByAmt(caseList,distReqVo.getAmountType());
          log.info("preDistCase amList size={}", amtList.size());
          InitDistList(distReqVo.getDismode(), distResVoList,amtList,distReqVo.getGrpList(),distReqVo.getDisablelastgrp());
          //预分配。
          procSnakeDistribute(distReqVo,distResVoList,amtList);
          //按照输入顺序重新排序。
          distResVoList.sort(Comparator.comparing(DistResVo::getId));
          return distResVoList;
      }
        private List<DistResVo> preAssginCase(Integer distMode, List<Integer> caseList, List<ValRadioVo> keyList,Integer pridelay,boolean isUsrDelay)
        {
        //按金额比例分配模式预分案
        if(pridelay==null)
            pridelay=1;

        List<DistResVo> distResVoList=new LinkedList<>();
        List<DistCreditcaseVo> amtList= caseSortByAmt(caseList);

        log.info("preDistCase:CaseCount={},pridelay={}", caseList.size(),pridelay);

        InitDistList(distMode, distResVoList, amtList, keyList, null);
        //暂且不考虑留案情况。20250407
        //if(pridelay==1) {//是否需要考虑留案情况
        //    distBuildDelay(distResVoList, amtList,isUsrDelay,true);
        //}
        procSnakeDistribute(distMode,distResVoList,amtList);
        distResVoList.sort(Comparator.comparing(DistResVo::getId));
        return distResVoList;
    }
    private int InitDistList(Integer distMode,
                                List<DistResVo> distResVoList,
                             List<DistCreditcaseVo> amtList,
                             List<ValRadioVo> keyList, Integer disableLastGrp)
    {
        int casecount=amtList.size();
        double radio=0.0,lastradio=0.0;
        int precount=0;
        BigDecimal preamount=new BigDecimal(0.00);
        BigDecimal totalAmt=GetTotalAmount(amtList);
        if( distMode == 1) {
            //Collections.shuffle(keyList);
            radio = Math.round((100.0 / keyList.size())*100)/100.0; //计算平均比例,保留2位小数
            for( int i = 0; i< keyList.size(); i++)
            {
                DistResVo distResVo = new DistResVo();
                distResVo.setKey(keyList.get(i).getKey());
                distResVo.setId(i);
                distResVo.setPreradio(radio);
                distResVo.setRadio(radio);

                distResVo.setPreamount(totalAmt.multiply(new BigDecimal(radio/100.0)).setScale(2,2));
                distResVo.setPrecount( casecount/keyList.size() + ( casecount % keyList.size() > i ? 1:0 ));
                distResVo.setDelaycount(0);
                distResVo.setCount(0);
                distResVo.setAmount(new BigDecimal("0.00"));
                distResVo.setDelayamt(new BigDecimal("0.00"));
                distResVo.setDistratio(0.0);
                distResVo.setFicaseList(new LinkedList<>());
                distResVoList.add(distResVo);
            }
            Collections.shuffle(distResVoList);
            for( int i = 0; i< keyList.size(); i++) {
                distResVoList.get(i).setTargetNum1(casecount/keyList.size() + ( casecount % keyList.size() > i ? 1:0 ));
                distResVoList.get(i).setTargetNum2(0);
                distResVoList.get(i).setTargetNum3(0);
                log.info("InitDistList:CaseCount={},DistGroupCount={},totalAmt={},distmode={},keyId={},setTargetNum1={}",
                        amtList.size(),keyList.size(),totalAmt,distMode,distResVoList.get(i).getKey(),distResVoList.get(i).getTargetNum1()
                );
            }
        }
        else
        {
            //检查分成三段的比例。
            int totalRatio = 0;
            int ratio1 = 0, ratio2 =0, ratio3=0;

            for(int i=0; i<keyList.size();i++)
                totalRatio += keyList.get(i).getRadio();

            for(int i=0; i< keyList.size(); i++)
            {
                DistResVo distResVo = new DistResVo();
                distResVo.setKey(keyList.get(i).getKey());
                distResVo.setPreradio(keyList.get(i).getRadio());
                distResVo.setRadio(keyList.get(i).getRadio());
                precount =(int)Math.round(keyList.get(i).getRadio() *casecount / totalRatio);
                preamount=totalAmt.multiply(new BigDecimal(1.0 * keyList.get(i).getRadio()/totalRatio)).setScale(2,2);//保留2位小数
                distResVo.setPrecount(precount);
                distResVo.setPreamount(preamount);
                distResVo.setDelaycount(0);
                distResVo.setCount(0);
                distResVo.setAmount(new BigDecimal("0.00"));
                distResVo.setDelayamt(new BigDecimal("0.00"));
                distResVo.setDistratio(0.0);
                distResVo.setTargetNum1(0);
                distResVo.setTargetNum2(0);
                distResVo.setTargetNum3(0);
                distResVo.setFicaseList(new LinkedList<>());
                distResVo.setId(i);
                distResVoList.add(distResVo);
            }
            keyList.sort(Comparator.comparing(ValRadioVo::getRadio));
            int minRatio = keyList.get(0).getRadio();
            if( ObjectUtils.isNotNull(disableLastGrp) && disableLastGrp == 1){
                ratio1 = 100;
                ratio2 = 0;
                ratio3 = 0;
            }
            else if( minRatio * casecount / totalRatio >=4 ) {
                ratio1 = 25;
                ratio2 = 50;
                ratio3 = 25;
            }
            else if( minRatio * casecount / totalRatio >=3 ){
                //30% 40%, 30%
                ratio1 = 30;
                ratio2 = 40;
                ratio3 = 30;
            }
            else if( minRatio * casecount / totalRatio >=2 ){
                ratio1 = 50;
                ratio2 = 50;
                ratio3 = 0;
            }
            else {
                // 100%
                ratio1 = 100;
                ratio2 = 0;
                ratio3 = 0;
            }
            Integer distCnt=0, totalDistCnt1=0, totalDistCnt2=0, totalDistCnt3 = 0;
            Integer targetCnt1 = casecount * ratio1 / 100;

            log.debug( "第一轮待分配的总数：" + targetCnt1);
            Collections.shuffle(distResVoList);
            for (int i = 0; i < distResVoList.size(); i++) {

                distCnt = targetCnt1 * (int)distResVoList.get(i).getPreradio()/totalRatio;
                distResVoList.get(i).setDistDivideNum( 1.0 * targetCnt1 * (int)distResVoList.get(i).getPreradio()/totalRatio - distCnt);
                distResVoList.get(i).setTargetNum1(distCnt);
                log.debug( "第一轮组织：{}，比例：{}，计划分配数量：{}", distResVoList.get(i).getKey(),distResVoList.get(i).getRadio(), distCnt);
                totalDistCnt1 += distCnt;
            }
            //处理余数
            distResVoList.sort(Comparator.comparing(DistResVo::getDistDivideNum).reversed());
            for (int i = 0; i < targetCnt1 - totalDistCnt1; i++) {
                distResVoList.get(i).setTargetNum1(distResVoList.get(i).getTargetNum1() + 1);
                log.debug( "第一轮组织：{}，计划分配数量：{}", distResVoList.get(i).getKey(), distResVoList.get(i).getTargetNum1());
            }
            Integer targetCnt2 = casecount * ratio2 / 100;
            if( ratio3 == 0 )
                targetCnt2 = casecount - targetCnt1;
            log.debug( "第二轮待分配的总数：{}，ratio:{} ",targetCnt2, ratio2);
            //第二轮
            Collections.shuffle(distResVoList);
            for (int i = 0; i < distResVoList.size(); i++) {

                distCnt = targetCnt2 * (int)distResVoList.get(i).getPreradio()/totalRatio;
                distResVoList.get(i).setDistDivideNum( 1.0 * targetCnt2 * (int)distResVoList.get(i).getPreradio()/totalRatio - distCnt);
                distResVoList.get(i).setTargetNum2(distCnt);
                log.debug( "第一轮组织：{}，比例：{}，计划分配数量：{}", distResVoList.get(i).getKey(),distResVoList.get(i).getRadio(), distCnt);
                totalDistCnt2 += distCnt;
            }

            //处理余数
            distResVoList.sort(Comparator.comparing(DistResVo::getDistDivideNum).reversed());
            for (int i = 0; i < targetCnt2 - totalDistCnt2; i++) {
                distResVoList.get(i).setTargetNum2(distResVoList.get(i).getTargetNum2() + 1);
                log.debug( "第二轮组织：{}，计划分配数量：{}", distResVoList.get(i).getKey(), distResVoList.get(i).getTargetNum2());
            }
            //第三轮 25%
            Integer targetCnt3 =  casecount - targetCnt1 - targetCnt2;
            Collections.shuffle(distResVoList);
            log.debug( "第三轮待分配的总数：" + targetCnt3);
            for (int i = 0; i < distResVoList.size(); i++) {
                distCnt = targetCnt3 * (int)distResVoList.get(i).getPreradio()/totalRatio;
                distResVoList.get(i).setDistDivideNum( 1.0 * targetCnt3 * (int)distResVoList.get(i).getPreradio()/totalRatio - distCnt);
                distResVoList.get(i).setTargetNum3(distCnt);
                log.debug( "第三轮组织：{}，比例：{}，计划分配数量：{}", distResVoList.get(i).getKey(),distResVoList.get(i).getRadio(), distCnt);
                totalDistCnt3 += distCnt;
            }
            //处理余数
            distResVoList.sort(Comparator.comparing(DistResVo::getDistDivideNum).reversed());
            for (int i = 0; i < targetCnt3 - totalDistCnt3; i++) {
                distResVoList.get(i).setTargetNum3(distResVoList.get(i).getTargetNum3() + 1);
                log.debug( "第三轮组织：{}，计划分配数量：{}", distResVoList.get(i).getKey(), distResVoList.get(i).getTargetNum3());
            }

        }
        for (int i = 0; i < distResVoList.size(); i++) {
            distResVoList.get(i).setTargetDistNum(new Integer[]{distResVoList.get(i).getTargetNum1(),distResVoList.get(i).getTargetNum2(),distResVoList.get(i).getTargetNum3()});
        }
         return distResVoList.size();
    }

//    private int snakeDistribute(List<DistResVo> distResVoList,List<FiCreditcases> amtList,boolean isamtradio)
//    {//改进型蛇型分配算法
//        int snakemodepos=0;
//        int distgrpcount=distResVoList.size();
//
//        int maxpos=0;
//        int startpos=0,startpos2=amtList.size()-1;
//        log.info("snakeDistribute:CaseCount={},distgrpcount={},startpos2={}",amtList.size(),distgrpcount,startpos2);
//        BigDecimal disttotalamt=new BigDecimal(0.0);
//        double dAmt=0.0;
//        int kkcnt=0;
//        for(int k=0;k<amtList.size();k+=0){
//            if(snakemodepos % 2 ==0) {
//                for (int i = 0; i < distResVoList.size(); i++) {
//                    if(kkcnt>=amtList.size()){
//                        //2024-09-20 分配的案件比组数量少，比如2个案件分给3个组
//                        break;
//                    }
//                    FiCreditcases pCase=amtList.get(startpos);
//                    DistResVo distResVo = distResVoList.get(i);
//                    if(isamtradio){//按金额比例分配
//                        if(distResVo.getAmount().compareTo(distResVo.getPreamount())<0){
//                            dAmt=SetDistResValue(distResVo, pCase,false);
//                            disttotalamt=disttotalamt.add(new BigDecimal(dAmt));
//                            startpos++;
//                            k++;
//                            kkcnt++;
//                        }
//                    }
//                    else if(distResVo.getCount()<distResVo.getPrecount()) {//如果大于等于数量，就不能再取了
//                        dAmt=SetDistResValue(distResVo, pCase,false);
//                        disttotalamt=disttotalamt.add(new BigDecimal(dAmt));
//                        startpos++;
//                        k++;
//                        kkcnt++;
//                    }
//                }
//            }
//            else{
//                for (int i = 0; i < distResVoList.size(); i++) {
//                    if(kkcnt>=amtList.size()){
//                        //2024-09-20 分配的案件比组数量少，比如2个案件分给3个组
//                        break;
//                    }
//                    FiCreditcases pCase=amtList.get(startpos2);
//                    DistResVo distResVo = distResVoList.get(i);
//                    if(isamtradio){//按金额比例分配
//                        if(distResVo.getAmount().compareTo(distResVo.getPreamount())<0){
//                            dAmt=SetDistResValue(distResVo, pCase,false);
//                            disttotalamt=disttotalamt.add(new BigDecimal(dAmt));
//                            startpos2--;
//                            k++;
//                            kkcnt++;
//                        }
//                    }
//                    else if(distResVo.getCount()<distResVo.getPrecount()) {
//                        dAmt=SetDistResValue(distResVo, pCase,false);
//                        disttotalamt=disttotalamt.add(new BigDecimal(dAmt));
//                        startpos2--;
//                        k++;
//                        kkcnt++;
//                    }
//                }
//            }
//            snakemodepos++;
//            maxpos++;
//            if(maxpos>=JPubUtils.distMaxCount){//避免死循环
//                log.warn("snakeDistribute.fail:maxpos={},k={},startpos={},startpos2={},maxpos={},totalamt={}",
//                        maxpos,k,startpos,startpos2,maxpos,disttotalamt
//                );
//                throw new ServiceException(EnumRCode.DistCaseFail.value, EnumRCode.DistCaseFail.name);
//            }
//        }
//
//        int disttotal=amtList.size();
//        log.info("snakeDistribute:startpos={}/{},maxpos={},snakemodepos={},kkcnt={},disttotal={},disttotalamt={}",
//                startpos,startpos2,maxpos,snakemodepos,kkcnt,disttotal,disttotalamt);
//        //计算实际的分配比例（案件数比例和案件金额比例）
//        for (DistResVo distResVo : distResVoList) {
//            double amt=distResVo.getAmount().doubleValue();
//            //int r=(int)Math.round((amt*100.0)/disttotalamt.doubleValue());
//            double r=(amt*100.0)/disttotalamt.doubleValue();
//            distResVo.setRadioamt(String.format("%.2f",r));
//            //r=Math.round(distResVo.getCount()*100/disttotal);
//            r=distResVo.getCount()*100.0/disttotal;
//            distResVo.setRadiocount(String.format("%.2f",r));
//            if(log.isDebugEnabled()) {
//                log.debug("snakeDistribute:{}", distResVo);
//            }
//            else{
//                log.info("snakeDistribute({}):count={},amount={},delaycount={},delayamt={},radio={},radiocount={},radioamt={},precount={},preamount={},ficasecount={}",
//                        distResVo.getKey(),distResVo.getCount(),distResVo.getAmount(),distResVo.getDelaycount(),distResVo.getDelayamt(),distResVo.getRadio(),
//                        distResVo.getRadiocount(),distResVo.getRadioamt(),distResVo.getPrecount(),distResVo.getPreamount(),distResVo.getFicaseList().size()
//                );
//            }
//        }
//
//        amtList.clear();
//        return disttotal;
//    }
private int procSnakeDistribute(DistReqVo cfgActivity, List<DistResVo> grplist,List<DistCreditcaseVo> amtList)
{
    int snakemodepos=0;
    int distgrpcount=grplist.size();
    int maxpos=0;
    int startpos=0,startpos2=amtList.size();

    int disableLastGrp = 0;
    if( ObjectUtils.isNotNull(cfgActivity.getDisablelastgrp()) && cfgActivity.getDisablelastgrp() == 1)
        disableLastGrp = 1;
    log.info("Start Distribute CaseCount={},distgrpcount={}, disableLastGrp={}",amtList.size(),distgrpcount,disableLastGrp);


    BigDecimal disttotalamt=new BigDecimal(0.0);
    double dAmt=0.0;
    int kkcnt=0;
    int totalCnt = amtList.size();
    int needCnt = 0;
    int readCount = 0;

    List<DistCreditcaseVo> distCreditcaseVos = null;
//        List<DistObjectVo> distObjectVos = new ArrayList<>();
//        int agentCount = distgrpcount;
//        for( int i=0; i<agentCount;i++ )
//        {
//            DistObjectVo distObjectVo = new DistObjectVo();
//            distObjectVo.setKey(i);
//            distObjectVo.setRatio(grplist.get(i).getPreradio());
//            distObjectVo.setDistratio(0.0);
//            distObjectVos.add(distObjectVo);
//        }

    Collections.shuffle(grplist);
    grplist.sort(Comparator.comparing(DistResVo::getRadio).reversed());
    for( int loop=0; loop < 3; loop++) {
        needCnt = 0;
        for( int i=0; i< grplist.size(); i++)
            needCnt += grplist.get(i).getTargetDistNum()[loop];
        if( needCnt == 0)
            continue;
        distCreditcaseVos = getDistObject(startpos, needCnt, amtList);
        Collections.shuffle(distCreditcaseVos);
        for (int grpIndex = 0; grpIndex < grplist.size(); grpIndex++) {
            grplist.get(grpIndex).setDistNum(0);
        }

        if( distCreditcaseVos == null || distCreditcaseVos.size() == 0)
            continue;
        log.debug("loop:<{}>,size:{}",loop, distCreditcaseVos.size());
        Collections.shuffle(distCreditcaseVos);

        for (DistCreditcaseVo x : distCreditcaseVos) {
            boolean distributed = false;
            for (int grpIndex = 0; grpIndex < grplist.size(); grpIndex++) {

                if( grplist.get(grpIndex).getTargetDistNum()[loop] == 0)
                    continue;
                else if(grplist.get(grpIndex).getDistNum() >= grplist.get(grpIndex).getTargetDistNum()[loop])
                    continue;
                if( disableLastGrp == 1 && StringUtils.isNotEmpty(x.getLastSendToDept()) && grplist.get(grpIndex).getKey().compareTo(x.getLastSendToDept()) == 0)
                    continue;
                x.setDistGrpId(grplist.get(grpIndex).getKey());
                //grplist.get(grpIndex).setNum(grplist.get(grpIndex).getNum() + 1);
                grplist.get(grpIndex).setDistNum(grplist.get(grpIndex).getDistNum() + 1);
                //grplist.get(grpIndex).setAmount(grplist.get(grpIndex).getAmount().add(x.getOverdueTotalamt()));
                dAmt = SetDistResValue(grplist.get(grpIndex), x, false);
                disttotalamt = disttotalamt.add(new BigDecimal(dAmt));
                distributed = true;
                break;
            }
            //出现无法分配的记录
            if( !distributed ){
                for (DistResVo autoDistribute : grplist) {
                    autoDistribute.setOverRatio(1.0 * autoDistribute.getCount()/autoDistribute.getPrecount());
                }
                grplist.sort(Comparator.comparing(DistResVo::getOverRatio));
                for (DistResVo autoDistribute : grplist) {
                    if( autoDistribute.getKey().equals(x.getLastSendToDept()))
                        continue;
                    //autoDistribute.setNum(autoDistribute.getNum() + 1);
                    //autoDistribute.setAmount(autoDistribute.getAmount().add(x.getOverdueTotalamt()));
                    x.setDistGrpId(autoDistribute.getKey());
                    dAmt = SetDistResValue(autoDistribute, x, false);
                    disttotalamt = disttotalamt.add(new BigDecimal(dAmt));
                    break;
                }
            }
            log.debug("caseid:<{}>,ditgrp:{}",x.getCreditcaseid(),x.getLastSendToDept());
        }
        startpos += needCnt;
        distCreditcaseVos.clear();
    }
    //计算每个机构的比例，确认是否需要调整。
    for (DistResVo autoDistribute : grplist) {
        autoDistribute.setOverRatio(1.0 * autoDistribute.getAmount().doubleValue()/autoDistribute.getPreamount().doubleValue());

    }
    while(true) {
        grplist.sort(Comparator.comparing(DistResVo::getOverRatio).reversed());
        if (grplist.get(0).getOverRatio() > 1.05 || grplist.get(grplist.size()-1).getOverRatio()< 0.95)
        {
            //调整 调转方案，最大比例和最小比例间调转。
            grplist.get(0).getFicaseList().sort(Comparator.comparing(DistCreditcaseVo::getOverdueTotalamt).reversed());
            grplist.get(grplist.size()-1).getFicaseList().sort(Comparator.comparing(DistCreditcaseVo::getOverdueTotalamt).reversed());
            int ret = swapCase(disableLastGrp,grplist.get(0),grplist.get(grplist.size()-1));
            if( ret < 0 ){
                log.info("swap Error: ret={},exit.",ret);
                break;
            }
        }
        else
            break;
    }


//        for(int loop= 0; loop<3; loop++){
//            needCnt = 0;
//            for( int i=0; i< distResVoList.size(); i++)
//                needCnt += distResVoList.get(i).getTargetDistNum()[loop];
//            if( needCnt == 0)
//                continue;
//            distCreditcaseVos = getDistObject(startpos, needCnt, amtList);
//            Collections.shuffle(distCreditcaseVos);
//            for (int grpIndex = 0; grpIndex < distResVoList.size(); grpIndex++) {
//                distResVoList.get(grpIndex).setDistNum(0);
//            }
//            for (FiCreditcases x : distCreditcaseVos) {
//                for (int grpIndex = 0; grpIndex < distResVoList.size(); grpIndex++) {
//                    if(  distResVoList.get(grpIndex).getDistNum() >=  distResVoList.get(grpIndex).getTargetDistNum()[loop])
//                        continue;
//
//                    distResVoList.get(grpIndex).setDistNum(distResVoList.get(grpIndex).getDistNum()+1);
//                    dAmt = SetDistResValue(distResVoList.get(grpIndex), x, false);
//                    disttotalamt = disttotalamt.add(new BigDecimal(dAmt));
//                    break;
//                }
//
//
//            }
//            startpos += needCnt;
//            distCreditcaseVos.clear();
//        }

    int disttotal=amtList.size();
    log.info("snakeDistribute:startpos={}/{},maxpos={},snakemodepos={},kkcnt={},disttotal={},disttotalamt={}",
            startpos,startpos2,maxpos,snakemodepos,kkcnt,disttotal,disttotalamt);
    //计算实际的分配比例（案件数比例和案件金额比例）
    for (DistResVo distResVo : grplist) {
        double amt=distResVo.getAmount().doubleValue();
        //int r=(int)Math.round((amt*100.0)/disttotalamt.doubleValue());
        double r=(amt*100.0)/disttotalamt.doubleValue();
        distResVo.setRadioamt(String.format("%.2f",r));
        //r=Math.round(distResVo.getCount()*100/disttotal);
        r=distResVo.getCount()*100.0/disttotal;
        distResVo.setRadiocount(String.format("%.2f",r));
        if(log.isDebugEnabled()) {
            log.debug("snakeDistribute:{}", distResVo);
        }
        else{
            log.info("snakeDistribute({}):count={},amount={},delaycount={},delayamt={},radio={},radiocount={},radioamt={},precount={},preamount={},ficasecount={}",
                    distResVo.getKey(),distResVo.getCount(),distResVo.getAmount(),distResVo.getDelaycount(),distResVo.getDelayamt(),distResVo.getRadio(),
                    distResVo.getRadiocount(),distResVo.getRadioamt(),distResVo.getPrecount(),distResVo.getPreamount(),distResVo.getFicaseList().size()
            );
        }
    }
    amtList.clear();
    return disttotal;
}
    private int procSnakeDistribute(Integer fd, List<DistResVo> grplist,List<DistCreditcaseVo> amtList)
    {
        int snakemodepos=0;
        int distgrpcount=grplist.size();
        int maxpos=0;
        int startpos=0,startpos2=amtList.size();
        log.info("Start Distribute CaseCount={},distgrpcount={}",amtList.size(),distgrpcount);


        BigDecimal disttotalamt=new BigDecimal(0.0);
        double dAmt=0.0;
        int kkcnt=0;
        int totalCnt = amtList.size();
        int needCnt = 0;
        int readCount = 0;

        List<DistCreditcaseVo> distCreditcaseVos = null;

        Collections.shuffle(grplist);
        grplist.sort(Comparator.comparing(DistResVo::getRadio).reversed());
        for( int loop=0; loop < 3; loop++) {
            needCnt = 0;
            for( int i=0; i< grplist.size(); i++)
                needCnt += grplist.get(i).getTargetDistNum()[loop];
            if( needCnt == 0)
                continue;
            distCreditcaseVos = getDistObject(startpos, needCnt, amtList);
            Collections.shuffle(distCreditcaseVos);
            for (int grpIndex = 0; grpIndex < grplist.size(); grpIndex++) {
                grplist.get(grpIndex).setDistNum(0);
            }

            if( distCreditcaseVos == null || distCreditcaseVos.size() == 0)
                continue;
            log.debug("loop:<{}>,size:{}",loop, distCreditcaseVos.size());
            Collections.shuffle(distCreditcaseVos);

            for (DistCreditcaseVo x : distCreditcaseVos) {
                for (int grpIndex = 0; grpIndex < grplist.size(); grpIndex++) {
                    if( grplist.get(grpIndex).getTargetDistNum()[loop] == 0)
                        continue;
                    else if(grplist.get(grpIndex).getDistNum() >= grplist.get(grpIndex).getTargetDistNum()[loop])
                        continue;
                    x.setDistGrpId(grplist.get(grpIndex).getKey());
                    grplist.get(grpIndex).setDistNum(grplist.get(grpIndex).getDistNum() + 1);
                    dAmt = SetDistResValue(grplist.get(grpIndex), x, false);
                    disttotalamt = disttotalamt.add(new BigDecimal(dAmt));
                    break;
                }
                log.debug("caseid:<{}>,ditgrp:{}",x.getCreditcaseid(),x.getLastSendToDept());
            }
            startpos += needCnt;
            distCreditcaseVos.clear();
        }
        //计算每个机构的比例，确认是否需要调整。
        for (DistResVo autoDistribute : grplist) {
            autoDistribute.setOverRatio(1.0 * autoDistribute.getAmount().doubleValue()/autoDistribute.getPreamount().doubleValue());
        }
        while(true) {
            grplist.sort(Comparator.comparing(DistResVo::getOverRatio).reversed());
            if (grplist.get(0).getOverRatio() > 1.05 || grplist.get(grplist.size()-1).getOverRatio() < 0.95)
            {
                //调整 调转方案，最大比例和最小比例间调转。
                grplist.get(0).getFicaseList().sort(Comparator.comparing(DistCreditcaseVo::getOverdueTotalamt).reversed());
                grplist.get(grplist.size()-1).getFicaseList().sort(Comparator.comparing(DistCreditcaseVo::getOverdueTotalamt).reversed());
                int ret = swapCase(0, grplist.get(0),grplist.get(grplist.size()-1));
                if( ret < 0 ){
                    log.info("swap Error: ret={},exit.",ret);
                    break;
                }
            }
            else
                break;
        }

        int disttotal=amtList.size();
        log.info("snakeDistribute:startpos={}/{},maxpos={},snakemodepos={},kkcnt={},disttotal={},disttotalamt={}",
                startpos,startpos2,maxpos,snakemodepos,kkcnt,disttotal,disttotalamt);
        //计算实际的分配比例（案件数比例和案件金额比例）
        for (DistResVo distResVo : grplist) {
            double amt=distResVo.getAmount().doubleValue();
            //int r=(int)Math.round((amt*100.0)/disttotalamt.doubleValue());
            double r=(amt*100.0)/disttotalamt.doubleValue();
            distResVo.setRadioamt(String.format("%.2f",r));
            //r=Math.round(distResVo.getCount()*100/disttotal);
            r=distResVo.getCount()*100.0/disttotal;
            distResVo.setRadiocount(String.format("%.2f",r));
            if(log.isDebugEnabled()) {
                log.debug("snakeDistribute:{}", distResVo);
            }
            else{
                log.info("snakeDistribute({}):count={},amount={},delaycount={},delayamt={},radio={},radiocount={},radioamt={},precount={},preamount={},ficasecount={}",
                        distResVo.getKey(),distResVo.getCount(),distResVo.getAmount(),distResVo.getDelaycount(),distResVo.getDelayamt(),distResVo.getRadio(),
                        distResVo.getRadiocount(),distResVo.getRadioamt(),distResVo.getPrecount(),distResVo.getPreamount(),distResVo.getFicaseList().size()
                );
            }
        }
        amtList.clear();
        return disttotal;
    }
    private List<DistCreditcaseVo> getDistObject(int startPos, int needCount, List<DistCreditcaseVo> amList)
    {
       List<DistCreditcaseVo> distCreditcaseVos = new ArrayList<>();

       for(int i=0; i<needCount; i++) {
           distCreditcaseVos.add(amList.get(startPos + i));
       }
       return distCreditcaseVos;
    }

    /**
     * @param amtList
        * @return
     */
    private BigDecimal GetTotalAmount(List<DistCreditcaseVo> amtList)
    {
        BigDecimal amt=new BigDecimal(0.0);

        for (DistCreditcaseVo pCase : amtList) {
            if (pCase.getOverdueTotalamt() != null) {
                amt = amt.add(pCase.getOverdueTotalamt());
            }
        }

        return amt;
    }

//    private BigDecimal GetTotalAmount(List<FiCreditcases> amtList)
//    {
//        return GetTotalAmount(amtList,1);
//    }

    private double SetDistResValue(DistResVo distResVo,DistCreditcaseVo pCase,boolean isdelay)
    {
        DecimalFormat df=new DecimalFormat("#.00");
        double amt=JPubUtils.ObjectToDouble(pCase.getOverdueTotalamt());
        if(isdelay){
            distResVo.setDelaycount(distResVo.getDelaycount()+1);
            double d = distResVo.getDelayamt().doubleValue() + amt;
            distResVo.setDelayamt(new BigDecimal(df.format(d)));
        }else{
            distResVo.setCount(distResVo.getCount() + 1);
            double d = distResVo.getAmount().doubleValue() + amt;
            distResVo.setAmount(new BigDecimal(df.format(d)));
        }
        distResVo.getFicaseList().add(pCase);
        return amt;
    }

    private List<DistCreditcaseVo> caseSortByAmt(List<Integer> caseList)
    {//案件按已出账单欠款排序
       return  caseSortByAmt(caseList,3);
    }

    /**
     * distAmountType :1. 逾期金额，2：结清金额，3：分案金额。
     * @param caseList
     * @param distAmountType
     * @return
     */
    private List<DistCreditcaseVo> caseSortByAmt(List<Integer> caseList,Integer distAmountType)
    {//案件按已出账单欠款排序
        List<DistCreditcaseVo> distCreditcaseVoList = selectJoinList(DistCreditcaseVo.class,
                new MPJLambdaWrapper<FiCreditcases>()
                        .selectAll(FiCreditcases.class)
                        .in(FiCreditcases::getCreditcaseid, caseList)
                        .orderByDesc(distAmountType==1, FiCreditcases::getOverdueTotalamt)
                        .orderByDesc(distAmountType==2, FiCreditcases::getWaitAmount)
                        .orderByDesc(distAmountType==3,FiCreditcases::getCpTotalamt)
        );
        for( DistCreditcaseVo x : distCreditcaseVoList){

            if( distAmountType == 2) {
                x.setOverdueTotalamt(ObjectUtils.isNotNull(x.getWaitAmount())?x.getWaitAmount():BigDecimal.ZERO); ;
            }
            else if(distAmountType == 3){
                x.setOverdueTotalamt(ObjectUtils.isNotNull(x.getCpTotalamt())?x.getCpTotalamt():BigDecimal.ZERO);
            }
        }
        return distCreditcaseVoList;
    }
    private List<FiCreditcases> caseSortByCase(List<Integer> caseList)
    {//案件按已出账单欠款排序
        return selectJoinList(FiCreditcases.class,
                new MPJLambdaWrapper<FiCreditcases>()
                        .selectAll(FiCreditcases.class)
                        .in(FiCreditcases::getCreditcaseid, caseList)
                        .orderByAsc(FiCreditcases::getCreditcaseid)
        );
    }

//    private int distBuildDelay(List<DistResVo> distResVoList,List<FiCreditcases> amtList,boolean isUsrDelay,boolean isAmountMode)
//    {
//        int delaycount=0;
//        for(int i=amtList.size()-1;i>=0;i--){
//            FiCreditcases pCase=amtList.get(i);
//            String delaykey=pCase.getSendtodept();
//            if(JPubUtils.IsNotEmpty(delaykey)){
//                if(isUsrDelay){
//                    delaykey=pCase.getSendtousr();
//                    if(JPubUtils.IsEmpty(delaykey)){
//                        log.info("distBuildDelay({}):nofind sendtousr",pCase.getCreditcaseid());
//                        continue;
//                    }
//                }
//                for (DistResVo distResVo : distResVoList) {
//                    if(JPubUtils.IsEqual(delaykey,distResVo.getKey())){
//                        delaycount++;
//                        SetDistResValue(distResVo,pCase,true);
//                        amtList.remove(pCase);
//                    }
//                }
//            }
//        }
//
//        //没有留案数，不需要计算
//        if(delaycount<=0) {
//            log.info("distBuildDelay:nodelay");
//            return delaycount;
//        }
//
//        int sjcount=amtList.size(); //去掉留案户数剩余预分案总户数
//        double lastradio=0.0;
//        BigDecimal lastamt=new BigDecimal(0.0);
//        int poslist=0,lastcount=0;
//
//        BigDecimal totalAmount=new BigDecimal(0.0);
//        for(int i=0;i<amtList.size();i++){//去掉留案户数剩余预分案总金额
//            FiCreditcases pCase=amtList.get(i);
//            if(ObjectUtils.isNotNull(pCase.getOverdueTotalamt()))
//                totalAmount=totalAmount.add((BigDecimal)pCase.getOverdueTotalamt());
//        }
//        log.info("distBuildDelay:count={},amount={},delaycount={},isAmountMode={}",sjcount,totalAmount,delaycount,isAmountMode);
//
//        //修正比例，把留案数计算在内，2024-06-09
//        for (DistResVo distResVo : distResVoList) {
//            BigDecimal preAmount = distResVo.getPreamount().subtract(distResVo.getDelayamt());
//            int precount=distResVo.getPrecount()-distResVo.getDelaycount();
//            double radio = 0.0;
//            if(isAmountMode) {
//                //这样如果数据量太大，可能会引起divide Non-terminating decimal expansion 异常，需要增加BigDecimal.ROUND_CEILING，2024-10-15
//                //radio = (preAmount.multiply(new BigDecimal(100.0)).divide(totalAmount)).doubleValue();
//                radio = (preAmount.doubleValue()*100.0) / totalAmount.doubleValue();
//            }
//            else{
//                radio = (precount * 100.0) / sjcount;
//            }
//            radio=Math.round(radio*100)/100.0; //取2位小数
//            distResVo.setPreradio(radio);
//            distResVo.setPreamount(preAmount);
//            distResVo.setPrecount(precount);
//            if(poslist==distResVoList.size()-1){//设置最后一组
//                distResVo.setPreradio(100.0 - lastradio);
//                distResVo.setPrecount(sjcount-lastcount);
//                distResVo.setPreamount(totalAmount.subtract(lastamt));
//            }
//            log.info("distBuildDelay:{}.SetRadio={}/{},Count={}/{},Amt={}/{},delaycount={},delayamt={},ficaseList={}",
//                    distResVo.getKey(),distResVo.getRadio(),distResVo.getPreradio(),distResVo.getCount(),distResVo.getPrecount(),
//                    distResVo.getAmount(), distResVo.getPreamount(), distResVo.getDelaycount(),
//                    distResVo.getDelayamt(),distResVo.getFicaseList()
//            );
//            poslist++;
//            lastradio+=radio;
//            lastcount+=precount;
//            lastamt=lastamt.add(preAmount);
//        }
//
//        return delaycount;
//    }

//    private void BuildFiCaseAmt(FiCreditcases fi,LinkedHashMap p)
//    {
//        //分案，回收，出催是需要写入记录历史
//        if(p==null){//垃圾数据导致的，2024-06-18
//            log.warn("BuildFiCaseAmt:p isnull");
//        }else {
//            fi.setOdudPrin((BigDecimal) p.get("settled_bill_amt"));
//            fi.setOverPrin((BigDecimal) p.get("settled_bill_prin"));
//            fi.setInterestFee((BigDecimal) p.get("settled_bill_int"));
//            fi.setOthExpense((BigDecimal) p.get("settled_bill_fee"));
//            fi.setMthsOdue((Integer) (p.get("mths_odue")));
//            if(JPubUtils.IsEmpty(p.get("mths_day"))){//2024-07-28
//                fi.setMthsDay((Integer) (p.get("mths_day")));
//            }
//            if(JPubUtils.IsEmpty(p.get("repay"))) {//2024-08-02
//                fi.setRepay((BigDecimal) p.get("repay"));
//            }
//        }
//    }

    private int distToGroup(String distgrp,String $usrid,
                            String batchid,
                            Integer actiontype,
                            Date bindenddt,
                            FiCreditcases p,
                            Date distdt,
                            Date effectivedistday,
                            String caesFlag)
    {//委案时,退案时需要记录逾期期数，已出账单欠款，本金，利息，费用，委案期间记录：还款金额
        FiCreditcases fi = new FiCreditcases();
        BeanUtils.copyProperties(p,fi);
        Integer ficaseid=p.getCreditcaseid();
        //fi.setCreditcaseid(ficaseid);
        fi.setStatus(EnumFiCaseStatus.Distribute.value);
        fi.setStatusdt(new Date());
        fi.setCcc(EnumFiCaseQueue.Q.value);
        fi.setDistbatchid(batchid);
        fi.setBindenddt(bindenddt);
        fi.setDistdt(distdt);
        fi.setDistusr($usrid);
        fi.setDistributetype(actiontype);
        fi.setSendtodept(distgrp);
        fi.setCpStartTime(effectivedistday);
        fi.setCpEndTime(fi.getBindenddt());

        fi.setCpTotalamt(fi.getOverdueTotalamt());
        fi.setCpInterest(fi.getPreRepayInterest());
        fi.setCpAmt(fi.getPreRepayPrincipal());
        fi.setCpCost(fi.getPreRepayFee());
        fi.setCpOverdueDays(fi.getMthsDay());

        fi.setDisttag(caesFlag);
        if(JPubUtils.IsEmpty(p.getIndt())){
            fi.setIndt(new Date());
        }
//        fi.setRepay(null);
//        fi.setRepaydt(null);
//        fi.setAssigndt(null);
//        fi.setAssigntype(null);
//        fi.setAssignusr(null);
//        fi.setSendtousr(null);

        return baseMapper.update(fi,new UpdateWrapper<FiCreditcases>()
                .lambda()
                .eq(FiCreditcases::getCreditcaseid,ficaseid)
                .set(FiCreditcases::getRepay,null)
                .set(FiCreditcases::getRepaydt,null)
                .set(FiCreditcases::getAssigndt,null)
                .set(FiCreditcases::getAssigntype,null)
                .set(FiCreditcases::getAssignusr,null)
                .set(FiCreditcases::getSendtousr,null)
        );
        //return baseMapper.updateById(fi);
    }

    private int assginToUser(String assusr,String $usrid,String batchid,
                             Integer actiontype,FiCreditcases p,String assgrp,Date bindenddt,Date distdt
    )
    {
        FiCreditcases fi = new FiCreditcases();
        BeanUtils.copyProperties(p,fi);
        Integer ficaseid=p.getCreditcaseid();
        fi.setCreditcaseid(ficaseid);
        fi.setStatus(EnumFiCaseStatus.Assign.value);
        fi.setStatusdt(new Date());
        fi.setCcc(EnumFiCaseQueue.Q.value);
        if(JPubUtils.IsNotEmpty(assgrp)){
            //这种情况下直接派工（分配）给组下催员，主要针对内催组,assbatchid=distbatchid
            distToGroup(assgrp,$usrid,batchid,actiontype,bindenddt,p,distdt,null,null);
        }
        fi.setAssignbatchid(batchid);
        fi.setAssigndt(new Date());
        fi.setAssignusr($usrid);
        fi.setAssigntype(actiontype);
        fi.setSendtousr(assusr);
        int ret=baseMapper.updateById(fi);

        log.info("assginToUser:ficaseid={} To {} Ret={}",ficaseid,assusr,ret);

        return ret;
    }

    /**
     * 获取待分案的案件详情。
     * @param caseList
     * @return
     */
    private List<FiCreditcases> getDistCaseList(List<Integer> caseList)
    {
        //委案时时记录：逾期期数，已出账单欠款，己出账单欠款(当前余额)本金，己出账单欠款(当前余额)利息，己出账单欠款(当前余额)费用
        return selectJoinList(FiCreditcases.class,
                new MPJLambdaWrapper<FiCreditcases>()
                        .selectAll(FiCreditcases.class)
                        .eq(FiCreditcases::getCcc, EnumFiCaseQueue.O.value)

                        .in(FiCreditcases::getCreditcaseid, caseList)
                        .orderByDesc(FiCreditcases::getOverdueTotalamt)
        );
    }

    private List<FiCreditcases> getAssCaseList(List<Integer> caseList)
    {
        return selectJoinList(FiCreditcases.class,
                new MPJLambdaWrapper<FiCreditcases>()
                        .selectAll(FiCreditcases.class)
                        .in(FiCreditcases::getCcc, EnumFiCaseQueue.Q.value)
                        .in(FiCreditcases::getCreditcaseid, caseList)
        );
    }

    private LinkedHashMap getFiCaseAcctBal(Integer ficaseid)
    {
        return selectJoinOne(LinkedHashMap.class,
                new MPJLambdaWrapper<FiCreditcases>()
                        .selectAs("(fc_getpayamt(t.credit_acct, DATE_FORMAT(t.distdt,'%Y-%m-%d'), DATE_FORMAT(CURRENT_DATE,'%Y-%m-%d')))",FiCreditcases::getRepay)
                        .leftJoin(CreditAccountBalance.class,"cdb",CreditAccountBalance::getAcct, FiCreditcases::getCreditAcct)
                        .leftJoin(CreditAccount.class,"cda",CreditAccount::getAcct,FiCreditcases::getCreditAcct)
                        .select(CreditAccountBalance::getSettledBillAmt, CreditAccountBalance::getSettledBillPrin, CreditAccountBalance::getSettledBillInt,
                                CreditAccountBalance::getSettledBillFee)
                        .select(CreditAccount::getMthsOdue,CreditAccount::getMthsDay)
                        .eq(FiCreditcases::getCreditcaseid, ficaseid)
        );
    }
    private boolean doDistCaseTo(List<DistResVo> distResVoList,String batchid, String batchname, String usrid, DistReqVo paras)
    {

        // 获取当前日期和时间
        //Random random = new Random();
        //int id = random.nextInt(100);
        LocalDateTime currentDateTime = LocalDateTime.now();
        //String formatter = currentDateTime.format(DateTimeFormatter.ofPattern("yyMMddHHmm"));
        //String batchid = "A" + formatter + String.format("%03d", id);

        Date distdt = new Date();
        Date binddt = null;
        long diffInDays = 0;

        if (Objects.equals(paras.getDistdaymode(), "1")) {
            Calendar calendar = Calendar.getInstance();
            calendar.setTime(new Date());
            calendar.add(Calendar.MONTH, paras.getAftermonth());
            calendar.set(Calendar.DATE,Integer.parseInt(paras.getFixday()));

            binddt = calendar.getTime();
            SimpleDateFormat sdf = new SimpleDateFormat("dd");
            if( Integer.parseInt(sdf.format(binddt)) != Integer.parseInt(paras.getFixday()))
            {
                log.info("Bind date={}, config date={}",sdf.format(binddt),paras.getFixday());
                //获取上一个月的最后一天日期。
                calendar.set(Calendar.DAY_OF_MONTH, 1);
                calendar.add(Calendar.DATE, -1);
                binddt = calendar.getTime();
            }
            long diffInMillies = Math.abs(calendar.getTime().getTime() - distdt.getTime());
            diffInDays = TimeUnit.DAYS.convert(diffInMillies, TimeUnit.MILLISECONDS);

        } else {
            Calendar calendar = Calendar.getInstance();
            calendar.setTime(new Date());
            calendar.add(Calendar.DATE, paras.getBinddays());
            binddt = calendar.getTime();

        }
        String recycleType = paras.getRecycleType();
        Integer pRecycleType = 0;
        // 升期回收#降低回收#出崔回收#结清回收#停催回收
        if(StringUtils.isNotEmpty(recycleType) ){

            String[] s = recycleType.split("#");
            pRecycleType = Integer.parseInt(s[0]) * 100 + Integer.parseInt(s[1]) * 200 + Integer.parseInt(s[2]) * 10 +   Integer.parseInt(s[3]);
            if(s.length >= 5)
                pRecycleType += Integer.parseInt(s[4]) * 1000;
        }
        BigDecimal distamt=new BigDecimal(0.0);
        //更新分案案件记录。
        int distokcnt=0,distcount=0;
        String distdtime=JPubUtils.DTimeToString(new Date());
        CfgActivity cfgActivity = new CfgActivity();
        cfgActivity.setCaseflag(paras.getCaseflag());
        cfgActivity.setAmountType(paras.getAmountType());
        cfgActivity.setEffectivedistday(paras.getEffectivedistday());

        for (DistResVo distResVo : distResVoList) {//分案组
            List<DistCreditcaseVo> caseList=distResVo.getFicaseList();
            if( caseList.size() == 0)
                continue;
            for(DistCreditcaseVo x : caseList ){
                x.setDistGrpId( distResVo.getKey());
                if( ObjectUtils.isNotNull(x.getOverdueTotalamt()))
                    distamt = distamt.add(x.getOverdueTotalamt());
                distcount++;
                distokcnt +=  updateAutoDistCreditCase(usrid,
                        x,
                        "",
                        cfgActivity,
                        batchid,
                        distdt,
                        binddt,
                        2,
                        pRecycleType );
            }
        }

        //创建分案批次记录。
        FiDistbatchHist fiDistbatchHist = new FiDistbatchHist();
        fiDistbatchHist.setBatchid(batchid);
        fiDistbatchHist.setBatchname(batchname);
        fiDistbatchHist.setBinddays((int) diffInDays);
        fiDistbatchHist.setBindenddt(binddt);
        fiDistbatchHist.setActiontype("2");
        fiDistbatchHist.setActiondt(new Date());
        fiDistbatchHist.setActionusr(usrid);
        fiDistbatchHist.setOkcnt(distokcnt);
        fiDistbatchHist.setDistcnt(distcount);
        fiDistbatchHist.setDistamt(distamt);
        fiDistbatchHist.setDistmode(cfgActivity.getDistype2());
        fiDistbatchHistService.save(fiDistbatchHist);
        //增加分案历史记录。
        baseMapper.updateToHist(batchid,new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(distdt));
        return true;
    }

    private void SaveDistHist(String usrid,String batchid,String batchname,Integer binddays,
                              Integer distmode,Integer okcnt,Integer distcnt,String distdtime,BigDecimal distamt,Date distdt)
    {
        log.info("SaveDistHist:{},OkCnt={},distcnt={}",batchid,okcnt,distcnt);

        FiDistbatchHist hist=new FiDistbatchHist();
        hist.setBatchid(batchid);
        hist.setBatchname(batchname);
        hist.setActiondt(distdt);
        hist.setActionusr(usrid);
        hist.setActiontype("2"); //2=手动
        hist.setBinddays(binddays);

        hist.setDistmode(distmode);
        hist.setOkcnt(okcnt);
        hist.setDistcnt(distcnt);
        hist.setDistamt(distamt);
        fiDistbatchHistService.getBaseMapper().insert(hist);

        int ret=baseMapper.updateToHist(batchid,distdtime);
        log.info("SaveDistHist:{} Done,Ret={}",batchid,ret);
    }

    /**
     * 人工分案处理。
     * @param user
     * @param paras
     * @param distParas
     * @return
     */
    @Override
    public List<DistResVo> fiCaseDistribute(JSysUser user,DistReqVo paras,CrdCaseReqVo distParas)
    {
        Integer distmode=paras.getDismode();
        if(JPubUtils.IsEmpty(paras.getGrpList())){
            throw new ServiceException(EnumRCode.ArgErr.value, EnumRCode.ArgErr.name);
        }
        String batchid=paras.getBatchid();
        if(!CheckDistBindday(batchid,paras.getBinddays())){
            throw new ServiceException(EnumRCode.ArgErr.value, "选择历史批次时绑定天数不正确!");
        }
        String $usrid=user.getUsrid();
        String rkey = jCommonData.getDistResultKey($usrid);
        List<DistResVo> distResVoList = null;
        List<DistResVo> distResultList = null;
        if (paras.getIspredist() == PRE_OPERATE) { //预分案
            List<Integer> distCaseList = paras.getFicaseList();
            if (distParas == null) {
                //2025-06-09 使用查询条件取分配数据
                distParas = new CrdCaseReqVo();
                distParas.setPageNum(1);
                distParas.setPageSize(10);
            }
            else {
                distCaseList.clear();
            }
            bindFiCastList(distCaseList, user, distParas,paras);
            log.info("fiCaseDistribute:getDistCount={}", distCaseList.size());

            if(distCaseList.size()==0){
                throw new ServiceException(EnumRCode.NoFindPreDist.value, EnumRCode.NoFindPreDist.name);
            }
            //预分配
            distResVoList = preDistCase(distCaseList, paras);
            //2025-06-09 结果集可能会很大(比如使用查询条件模式取数），需要保存在redis里面，不能返回客户端
            boolean b = jCommonData.setData(rkey, distResVoList, JPubUtils.distExpiration);
            log.info("fiCaseDistribute.setPreDistResult({},{}):ret={}", rkey, JPubUtils.distExpiration, b);

            distResultList=buildDistResult(distResVoList);
        }
        else{
            String batchname=paras.getBatchname();
            if(JPubUtils.IsEmpty(batchid)){
                SimpleDateFormat sdf=new SimpleDateFormat("yyMMddHHmmss");
                batchid="M"+sdf.format(new Date());
            }
            if(JPubUtils.IsEmpty(batchname)){
                batchname=batchid;
            }

            Object o=jCommonData.getData(rkey);
            if(o==null){
                log.warn("fiCaseDistribute:nofinddata {}",rkey);
                throw new ServiceException(EnumRCode.NoFindPreDist.value, EnumRCode.NoFindPreDist.name);
            }else{
                distResVoList=(List<DistResVo>)o;
                jCommonData.delData(rkey);
            }

            //doDistCaseTo(distResVoList,batchid, $usrid, paras.getBinddays(), paras.getActiontype(), distmode, batchname);
            doDistCaseTo(distResVoList,batchid, batchname, $usrid, paras);

            distResultList=buildDistResult(distResVoList);
            distResVoList.clear();
        }

        return distResultList;
    }

    /**
     * 清除分案列表中案件信息，返回分案的数量。
     * @param distResVoList
     * @return
     */
    private List<DistResVo> buildDistResult(List<DistResVo> distResVoList)
    {//2025-06-09 结果集可能会很大(比如使用查询条件模式取数），不能把ficaseList返回到客户端
        List<DistResVo> resVos=new LinkedList<>();
        for(DistResVo distResVo : distResVoList){
            DistResVo rp=new DistResVo();
            BeanUtils.copyProperties(distResVo,rp);
            rp.getFicaseList().clear();
            rp.setFicaseList(null);
            resVos.add(rp);
        }
        return resVos;
    }

    private boolean doAssignCaseTo(List<DistResVo> distResVoList,String batchid,String $usrid,
                                   Integer actiontype,Integer distmode,String assgrp,Integer binddays) {
        if(JPubUtils.IsEmpty(binddays)||binddays<=0){
            binddays=90;
        }
        Date bindenddt = JPubUtils.addDays(new Date(), binddays);
        //assgrp不为空，表示直接派工
        log.info("doAssignCaseTo:distmode={},batchid={},binddays={}/{},grpid={}",
                distmode,batchid,binddays,bindenddt,assgrp
        );

        BigDecimal distamt=new BigDecimal(0.0);
        Date distdt=new Date();
        int distokcnt=0,distcount=0;
        String distdtime=JPubUtils.DTimeToString(new Date());
        for (DistResVo distResVo : distResVoList) {//分案账户（用户）LIST
            List<Integer> caseList=distResVo.getFicaseList().stream().map(DistCreditcaseVo::getCreditcaseid).collect(Collectors.toList());
            if( caseList.size() == 0) continue;
              distcount+=caseList.size();
            List<FiCreditcases> querylist=null;
            if(JPubUtils.IsEmpty(assgrp)){
                querylist = getAssCaseList(caseList);
            }else {//直接派工
                querylist = getDistCaseList(caseList);
            }
            for (int i=0;i<querylist.size();i++) {
                FiCreditcases pCase=querylist.get(i);
                BigDecimal amt=pCase.getOverdueTotalamt();
                if(amt!=null) {//2024-06-26
                    distamt=distamt.add(amt);
                }
                distokcnt += 1;
                assginToUser(distResVo.getKey(), $usrid, batchid, actiontype, pCase,assgrp,bindenddt,distdt);
            }
            log.info("doAssignCaseTo({}):distcount={},okcnt={},distamt={}/count={},amount={},Delaycount={},Delayamt={},radio={},Radiocount={},Radioamt={}",
                    distResVo.getKey(),caseList.size(),distokcnt,distamt,
                    distResVo.getCount(),distResVo.getAmount(),distResVo.getDelaycount(),distResVo.getDelayamt(),
                    distResVo.getRadio(),distResVo.getRadiocount(),distResVo.getRadioamt()

            );
            msgAlertsService.msgAlertAdd("派工提醒","有新派工案件",distResVo.getKey(),$usrid);

            if(JPubUtils.IsNotEmpty(assgrp)) {
                //直接派工成功后把留案取消
                fiDelayService.doCancel($usrid, caseList, EnumConfirmStatus.Pass.value);
            }
            querylist.clear();
        }

        if(JPubUtils.IsNotEmpty(assgrp)){
            SaveDistHist($usrid,batchid,"直接派工"+batchid,binddays,distmode,distokcnt,distcount,distdtime,distamt,distdt);
            //直接派工案件状态已经是分配状态了，需要把分案历史表案件状态改成由派工改成分配
            baseMapper.updateHistDistStatus(batchid);
        }
        int ret=baseMapper.updateToHistByAss(batchid);

        log.info("doAssignCaseTo Done,updateToHist:batchid={},ret={}",batchid,ret);

        return true;
    }

    @Override
    public List<DistResVo> fiCaseAssign(JSysUser user,AssReqVo paras,CrdCaseReqVo distParas)
    {//assreqvo.grpid 不为空表示直接派工
        Integer distmode=paras.getAssmode();
        if((distmode==DIST_MODE_CASERADIO || distmode==DIST_MODE_AMTRADIO)
                &&JPubUtils.IsEmpty(paras.getUsrList())){
            throw new ServiceException(EnumRCode.ArgErr.value, EnumRCode.ArgErr.name);
        }
        String $usrid=user.getUsrid();
        String rkey = jCommonData.getAssResultKey($usrid);
        List<DistResVo> distResVoList = null;
        List<DistResVo> distResultList = null;
        if(paras.getIspreass()==PRE_OPERATE) { //预览分配。
            List<Integer> distCaseList = paras.getFicaseList();
//            if (distParas != null) {//2025-06-09 使用查询条件取分配数据
//                bindFiCastList(distCaseList, user, distParas);
//                log.info("fiCaseAssign:getDistCount={}", distCaseList.size());
//            }
            if (distParas == null) {
                //2025-06-09 使用查询条件取分配数据
                distParas = new CrdCaseReqVo();
                distParas.setPageNum(1);
                distParas.setPageSize(15);
            }
            else {
                distCaseList.clear();
            }
            bindFiCastList(distCaseList, user, distParas);
            log.info("fiCaseAssign:getDistCount={}", distCaseList.size());
            distResVoList = preAssginCase(distmode, distCaseList, paras.getUsrList(),1,true);
            //2025-06-09 结果集可能会很大(比如使用查询条件模式取数），需要保存在redis里面，不能返回客户端
            boolean b = jCommonData.setData(rkey, distResVoList, JPubUtils.distExpiration);
            log.info("fiCaseAssign.setPreAssResult({},{}):ret={}", rkey, JPubUtils.distExpiration, b);
            distResultList=buildDistResult(distResVoList);
        }
        else{
            String batchid=paras.getBatchid();
            if(JPubUtils.IsEmpty(batchid)){
                SimpleDateFormat sdf=new SimpleDateFormat("yyMMddHHmmss");
                if(JPubUtils.IsNotEmpty(paras.getGrpid())){
                    //用于直接派工批次ID
                    batchid="P"+sdf.format(new Date());
                }else {//只用于分配批次ID
                    batchid = $usrid + "." + sdf.format(new Date());
                }
            }

            Object o=jCommonData.getData(rkey);
            if(o==null){
                log.warn("fiCaseAssign:nofinddata {}",rkey);
                throw new ServiceException(EnumRCode.NoFindPreDist.value, EnumRCode.NoFindPreDist.name);
            }else{
                distResVoList=(List<DistResVo>)o;
                jCommonData.delData(rkey);
            }

            doAssignCaseTo(distResVoList,batchid, $usrid,paras.getActiontype(), distmode,paras.getGrpid(),paras.getBinddays());
        }

        return distResultList;
    }

    private List<FiCreditcases> getRecyCaseList(List<Integer> caseList)
    {
        return selectJoinList(FiCreditcases.class,
                new MPJLambdaWrapper<FiCreditcases>()
                        .select(FiCreditcases::getCreditcaseid, FiCreditcases::getStatus, FiCreditcases::getCcc)
                        .in(FiCreditcases::getCcc, "Q","S")
                        .in(FiCreditcases::getCreditcaseid, caseList)
        );
    }

    @Override
    public CmdResVo fiCaseRecycle(JSysUser user, RecyReqVo paras,CrdCaseReqVo dataParas)
    {
        //增加权限控制RECY_MODE_TOP
        String $usrid=user.getUsrid();
        boolean oneRecycle=false;
        List<FiCreditcases> recylist=null;
        List<Integer> recylistID=paras.getFicaseList();
        if(dataParas!=null) {
            if (paras.getRecymode() == RECY_MODE_TOP) {
                if (jCommonData.IsRecycleProcess()) {
                    log.info("fiCaseRecycle.IsRecycleProcess...");
                    return new CmdResVo(0);
                }
                oneRecycle = true;
            }
//            else {
//                recylist = getRecyCaseList(recylistID);
//                log.info("fiCaseRecycle.recylist={},mode={}", recylist.size(), paras.getRecymode());
//            }
            recylistID = new LinkedList<>();
            bindFiCastList(recylistID, user, dataParas);
        }
        recylist = getRecyCaseList(recylistID);
        log.info("fiCaseRecycle.recylist={},mode={} batch", recylist.size(), paras.getRecymode());
        if(JPubUtils.IsEmpty(recylist))
            return new CmdResVo(0);
        Integer processCount=0;
        Date startrundt=new Date();
        SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss");
        String recybatchid=$usrid+"_"+sdf.format(startrundt);
        for(FiCreditcases fi:recylist){
            if(paras.getRecymode()==RECY_MODE_GROUP){//回收都本组,队列不改，只改状态
                fi.setStatus(EnumFiCaseStatus.Distribute.value);
                baseMapper.update(fi,new UpdateWrapper<FiCreditcases>()
                        .lambda()
                        .eq(FiCreditcases::getCreditcaseid,fi.getCreditcaseid())
                        .set(FiCreditcases::getSendtousr,null)
                        .set(FiCreditcases::getAppointdt,null)
                        .set(FiCreditcases::getAssignbatchid,null)
                        .set(FiCreditcases::getAssignid,null)
                        .set(FiCreditcases::getAssigntype,null)
                        .set(FiCreditcases::getAssignusr,null)
                        .set(FiCreditcases::getAssigndt,null)
                        .set(FiCreditcases::getStatusdt,new Date())
                        .set(FiCreditcases::getRecycledsc,paras.getRemark())
                        .set(FiCreditcases::getRecycledt,new Date())
                        .set(FiCreditcases::getRecycleusr,$usrid)
                );
                //更新到历史
                //1：分案，2：分配，3：回收，4：出催,  5: 坐席回收
                baseMapper.updateToHistByID(5, fi.getCreditcaseid());
                fiCreditcaseAssistantMapper.clearAssist(fi.getCreditcaseid());
            }
            else if(paras.getRecymode()==RECY_MODE_TOP){
                //停催队列可以回收，但是回收后还是停催状态，回收只清空绑定组和催员等信息，并不改变队列属性,停催属性只有通过停催管理或标签可以改变
                if( fi.getStatus() == EnumFiCaseStatus.Distribute.value || fi.getStatus() == EnumFiCaseStatus.Assign.value) {

                    fi.setStatus(EnumFiCaseStatus.Recycle.value);
                    fi.setStatusdt(new Date());
                }
                if (JPubUtils.IsEqual(fi.getCcc(), EnumFiCaseQueue.Q.value)) {
                    if( fi.getStatus() == EnumFiCaseStatus.Finish.value )
                    {
                        fi.setCcc(EnumFiCaseQueue.R.value);
                    }
                    else
                        fi.setCcc(EnumFiCaseQueue.O.value);
                }

                //这样也是可以更新的
                if(JPubUtils.IsNotEmpty(paras.getStopautodistday())){
                    Date dd=JPubUtils.addDays(new Date(),paras.getStopautodistday());
                    fi.setStopautodistenddt(dd);
                }

//                //2024-08-02 在getFiCaseAcctBal一起取出
//                //BigDecimal payamt=getPayAmount(fi.getCreditcaseid());
//                LinkedHashMap p=getFiCaseAcctBal(fi.getCreditcaseid());
//                BuildFiCaseAmt(fi,p);

                //PG里面需要这么写，不能重复一个字段SET

                baseMapper.update(fi,new UpdateWrapper<FiCreditcases>()
                        .lambda()
                        .eq(FiCreditcases::getCreditcaseid,fi.getCreditcaseid())
                        .set(FiCreditcases::getStatusdt,new Date())
                        .set(FiCreditcases::getRecycledsc,recybatchid)
                        .set(FiCreditcases::getRecycledt,new Date())
                        .set(FiCreditcases::getRecycleusr,$usrid)
                );
//                //先更新到历史
//                baseMapper.updateToHistByID(3, fi.getCreditcaseid());
//                //清空分案信息
//                baseMapper.update(fi,new UpdateWrapper<FiCreditcases>()
//                        .lambda()
//                        .eq(FiCreditcases::getCreditcaseid,fi.getCreditcaseid())
//                        .set(FiCreditcases::getSendtousr,null)
//                        .set(FiCreditcases::getAppointdt,null)
//                        .set(FiCreditcases::getAssignbatchid,null)
//                        .set(FiCreditcases::getAssignid,null)
//                        .set(FiCreditcases::getAssigntype,null)
//                        .set(FiCreditcases::getAssignusr,null)
//                        .set(FiCreditcases::getAssigndt,null)
//                        .set(FiCreditcases::getSendtodept,null)
//                        .set(FiCreditcases::getBindenddt,null)
//                        .set(FiCreditcases::getDistusr,null)
//                        .set(FiCreditcases::getDistdt,null)
//                        .set(FiCreditcases::getDistbatchid,null)
//                        .set(FiCreditcases::getDistributetype,null)
//                        .set(FiCreditcases::getDistributorid,null)
//                        .set(FiCreditcases::getLastccdt,null)
//                        .set(FiCreditcases::getLastcctaskid,null)
//                );
                processCount++;
                if(oneRecycle) {
                    jCommonData.SetRecycleProcess(processCount);
                }
                fiCreditcaseAssistantMapper.clearAssist(fi.getCreditcaseid());
            }
            else{
                throw new ServiceException(EnumRCode.ArgErr.value, EnumRCode.ArgErr.name);
            }
        }
        if(paras.getRecymode()==RECY_MODE_TOP){
            log.info("updateRecycle:startrundt={},usrid={},recybatchid={}",
                    JPubUtils.DTimeToString(startrundt),$usrid,recybatchid);
            baseMapper.insertRecycleHist(JPubUtils.DTimeToString(startrundt),$usrid,recybatchid,paras.getRemark());
            baseMapper.updateRecycleInit(JPubUtils.DTimeToString(startrundt),$usrid,recybatchid);
        }
        jCommonData.RemoveRecycleProcess();

        int recycount=recylist.size();
        fiDelayService.doCancel($usrid,recylistID,EnumConfirmStatus.Pending.value);

        recylist.clear();
        recylistID.clear();

        return new CmdResVo(recycount);
    }

    @Override
    public CmdResVo getCustFiCaseCount(String custnumber)
    {
        Long num=baseMapper.selectCount(new MPJLambdaWrapper<FiCreditcases>()
                .eq(FiCreditcases::getCustomernumber,custnumber)
                .in(FiCreditcases::getCcc,"O","Q","S")
        );

        return new CmdResVo(num.intValue(),custnumber);
    }

    @Override
    public CmdResVo getFiCaseReliefCount(Integer ficaseid)
    {
        Long num=baseMapper.selectCount(new MPJLambdaWrapper<FiCreditcases>()
                .leftJoin(FiRelief.class,FiRelief::getFicaseid,FiCreditcases::getCreditcaseid)
                .eq(FiRelief::getFicaseid,ficaseid)
                .in(FiRelief::getStatus,
                        EnumConfirmStatus.Pending.value,
                        EnumConfirmStatus.Pass.value,
                        EnumConfirmStatus.Finish.value
                )
        );

        return new CmdResVo(num.intValue(),ficaseid.toString());
    }

    @Override
    public int updateCallTask(Integer ficaseid,Integer taskid,Date appointdt)
    {
        if(JPubUtils.IsEmpty(ficaseid)) return 0;

        FiCreditcases fiCreditcases=new FiCreditcases();
        fiCreditcases.setCreditcaseid(ficaseid);
        fiCreditcases.setLastcctaskid(taskid);
        fiCreditcases.setLastccdt(new Date());
        if(appointdt!=null){
            fiCreditcases.setAppointdt(appointdt);
        }
        return baseMapper.updateById(fiCreditcases);
    }

    private void DoUpdateCase(String $usrid,Integer ficaseid,String acctno,CrdCmdResVo cmdResVo)
    {//案件出催
        if(JPubUtils.IsNotEmpty(cmdResVo.getMthsOdue())) {
            CreditAccountBalance balance = new CreditAccountBalance();
            balance.setSettledBillAmt(cmdResVo.getSettledBillAmt());
            balance.setLowestRepayBal(cmdResVo.getLowestRepayBal());
            if(cmdResVo.getUnsettledBillAmt()!=null){
                balance.setUnsettledBillAmt(cmdResVo.getUnsettledBillAmt());
            }
            if(cmdResVo.getInterestFee()!=null){
                balance.setInterestFee(cmdResVo.getInterestFee());
            }
            if(cmdResVo.getPenchgAcc()!=null){
                balance.setPenchgAcc(cmdResVo.getPenchgAcc());
            }
            if(cmdResVo.getOthExpense()!=null){
                balance.setOthExpense(cmdResVo.getOthExpense());
            }
            if(cmdResVo.getIntChgd()!=null){
                balance.setIntChgd(cmdResVo.getIntChgd());
            }
            if(cmdResVo.getPaymtClrd()!=null){
                balance.setPaymtClrd(cmdResVo.getPaymtClrd());
            }
            if(cmdResVo.getAge1Age6()!=null){
                balance.setAge1Age6(cmdResVo.getAge1Age6());
            }
            if(cmdResVo.getAcctDebtAll()!=null){
                balance.setAcctDebtAll(cmdResVo.getAcctDebtAll());
            }
            balance.setMddt(new Date());
            creditAccountBalanceMapper.update(balance,
                    new QueryWrapper<CreditAccountBalance>().eq("acct", acctno));

            CreditAccount account=new CreditAccount();
            if(cmdResVo.getMthsOdue()>=0) {
                //银数逾期期数不用減1， 通联逾期期数判断接口返回为9的时候，源期数保持不变，当为逾期期数小于9的时候使用接口参数減1
                //通联逾期期数=9,返回-1
                account.setMthsOdue(cmdResVo.getMthsOdue());
            }
            else{
                log.info("DoUpdateCase({}).MthsOdue={}",ficaseid,cmdResVo.getMthsOdue());
            }
            account.setMddt(new Date());
            creditAccountMapper.update(account,
                    new QueryWrapper<CreditAccount>().eq("acct",acctno));
        }
        if(cmdResVo.getIsRpc()==1){//出催
            LinkedHashMap p=getFiCaseAcctBal(ficaseid);
            //FiCreditcases fi=new FiCreditcases();
            FiCreditcases fi = getById(ficaseid);
            //fi.setCreditcaseid(ficaseid);
            //BuildFiCaseAmt(fi,p);

            //2024-08-02 一起放在BuildFiCaseAmt处理了
            //BigDecimal payamt=getPayAmount(ficaseid);
            //fi.setRepay(payamt);

            fi.setStatus(EnumFiCaseStatus.Finish.value);
            fi.setStatusdt(new Date());
            if(!fi.getCcc().equals(EnumFiCaseQueue.S.value))
                fi.setCcc(EnumFiCaseQueue.R.value);
            fi.setCloseusr($usrid);
            fi.setClosedt(new Date());
            fi.setMdusr($usrid);
            fi.setMddt(new Date());
            fi.setAppointdt(null);

            updateById(fi);
            //1：分案，2：分配，3：回收，4：出催,  5: 部门回收
            baseMapper.updateToHistByID(4, ficaseid);

            List<Integer> list=new LinkedList<>();
            list.add(ficaseid);
            fiDelayService.doCancel($usrid,list,EnumConfirmStatus.Pass.value);
        }
    }

    @Override
    public CrdCmdResVo refreshBalance(JSysUser user,CrdRefreshBalVo paras)
    {
        CrdCmdResVo resVo=new CrdCmdResVo();
        if(paras.getType()==2){
            if(!JUsrPerm.hasPermission(user,"division")){ //待分案菜单
                throw new ServiceException(EnumRCode.Forbidden.value, EnumRCode.Forbidden.name);
            }
            List<LinkedHashMap> casesList=selectJoinList(LinkedHashMap.class,
                    new MPJLambdaWrapper<FiCreditcases>()
                            .innerJoin(CreditAccount.class,"ct",CreditAccount::getAcct,FiCreditcases::getCreditAcct)
                            .select(CreditAccount::getCustNo,CreditAccount::getCrdtCardRepeNo,CreditAccount::getAcct)
                            .select(FiCreditcases::getCreditcaseid,FiCreditcases::getProdtype)
                            .in(FiCreditcases::getProdtype,"1","2")
                            .eq("ccc",EnumFiCaseQueue.O.value)
            );
            log.info("refreshBalance:count={}",casesList.size());
            if(casesList.size()>=100){
                resVo.setTotal(casesList.size());
                resVo.setRespCode("0");
                resVo.setRespCodeDesc("数据量太多，禁止全量手动刷新!");
                return resVo;
            }
            int okcount=0,rpccount=0;
            for(LinkedHashMap cases : casesList){
                CrdCmdResVo cmdResVo=CrdCommand.cardAcctBusiness(cases.get("cust_no").toString(),
                        cases.get("crdt_card_repe_no").toString(),
                        paras.getCurrency()
                );
                if(JPubUtils.IsEqual(cmdResVo.getRespCode(),"0")){
                    okcount++;
                    Integer ficaseid=JPubUtils.StringToInt(cases.get("creditcaseid").toString());
                    DoUpdateCase(user.getUsrid(),ficaseid,cases.get("acct").toString(),cmdResVo);
                    if(cmdResVo.getIsRpc()==1){
                        rpccount++;
                    }
                }
            }
            resVo.setRpccount(rpccount);
            resVo.setTotal(casesList.size());
            resVo.setOkcount(okcount);
            resVo.setRespCode("0");
            resVo.setRespCodeDesc("success");
        }else if(JPubUtils.IsEmpty(paras.getFicaseid())){
            throw new ServiceException(EnumRCode.ArgErr.value, EnumRCode.ArgErr.name);
        }
        else {
            FiCreditcases fi = baseMapper.selectById(paras.getFicaseid());
            //出催队列不在允许刷新余额,出催也可以刷余额，2024-06-25
            //if (JPubUtils.IsEqual(fi.getCcc(), EnumFiCaseQueue.R.value)) {
            //    resVo.setTotal(0);
            //    resVo.setIsRpc(1);
            //     resVo.setRespCode("0");
            //    resVo.setRespCodeDesc("案件已出催!");
            //} else{
            CreditAccount account=creditAccountMapper.selectOne(
                    new QueryWrapper<CreditAccount>().eq("acct",fi.getCreditAcct()));
            String cardno=account.getCrdtCardRepeNo();
            resVo = CrdCommand.cardAcctBusiness(fi.getCustomernumber(),
                    cardno, paras.getCurrency()
            );
            if(JPubUtils.IsEqual(resVo.getRespCode(), "0")) {
                DoUpdateCase(user.getUsrid(), paras.getFicaseid(), fi.getCreditAcct(), resVo);

                if(resVo.getMthsOdue()<0){//2024-06-06
                    resVo.setMthsOdue(account.getMthsOdue());
                }
            }
            resVo.setTotal(1);
            //}
        }
        return resVo;
    }

    @Override
    public CrdCmdResVo refreshNoOutBill(JSysUser user,CrdRefNOutBillVo paras)
    {
        FiCreditcases fi=baseMapper.selectById(paras.getFicaseid());
        CreditAccount account=creditAccountMapper.selectOne(
                new QueryWrapper<CreditAccount>().eq("acct",fi.getCreditAcct()));
        String cardno=account.getCrdtCardRepeNo();
        String acctno=fi.getCreditAcct();
        log.info("refreshNoOutBill:{}->{}",acctno,cardno);
        CrdCmdResVo resVo=null;
        if(JPubUtils.IsEqual(fi.getProdtype(),"1")){//银数
            resVo=CrdCommand.notOutBillDetailYs(cardno,null);
        }
        else{
            resVo=CrdCommand.notOutBillDetailTl(cardno);
        }
        accountNoutbillMapper.delete(new QueryWrapper<CreditAccountNoutbill>()
                .eq("acct", acctno)
        );
        if (JPubUtils.IsNotEmpty(resVo.getBillTailList())) {
            resVo.setTotal(resVo.getBillTailList().size());
            for(CrdBillTailResVo tailResVo : resVo.getBillTailList()){
                CreditAccountNoutbill def=new CreditAccountNoutbill();
                BeanUtils.copyProperties(tailResVo,def);
                def.setCardNbr(tailResVo.getCardNo());
                def.setDataDt(new Date());
                def.setAcct(acctno);
                def.setIsnotbill(1);
                log.info("refreshNoOutBill.save:{}",def);
                accountNoutbillMapper.insert(def);
            }
        } else resVo.setTotal(0);

        if(JPubUtils.IsEqual(fi.getProdtype(),"2")) {//通联
            refreshNotBill(acctno,cardno);
        }

        return resVo;
    }

    @Override
    public CmdResVo getProdType(Integer ficaseid)
    {
        FiCreditcases fi=baseMapper.selectById(ficaseid);
        if(fi==null){
            return new CmdResVo(0,"nofindficase");
        }
        return new CmdResVo(1,fi.getProdtype());
    }

    @Override
    public List<Integer> getCaseIDList(String custno)
    {
        List<Integer> list=new LinkedList<>();
        List<FiCreditcases> fiCreditcases=baseMapper.selectList(new QueryWrapper<FiCreditcases>()
                .eq("customernumber",custno)
        );
        if(JPubUtils.IsNotEmpty(fiCreditcases)){
            for (FiCreditcases fi : fiCreditcases){
                list.add(fi.getCreditcaseid());
            }
        }

        return list;
    }

    @Override
    public void refreshNotBill(String acctno,String cardno)
    {
        log.info("refreshNotBill:{}->{}",acctno,cardno);
        CrdCmdResVo resVo=CrdCommand.notBillDetailTl(cardno);

        if (JPubUtils.IsNotEmpty(resVo.getBillTailList())) {
            resVo.setTotal(resVo.getBillTailList().size());
            for(CrdBillTailResVo tailResVo : resVo.getBillTailList()){
                CreditAccountNoutbill def=new CreditAccountNoutbill();
                BeanUtils.copyProperties(tailResVo,def);
                def.setCardNbr(tailResVo.getCardNo());
                def.setDataDt(new Date());
                def.setAcct(acctno);
                def.setIsnotbill(0);
                log.info("refreshNotBill.save:{}",def);
                accountNoutbillMapper.insert(def);
            }
        }
    }

    private boolean CheckDistBindday(String batchid,Integer binddays)
    {
        //暂且不检查。
        return true;
//        if(JPubUtils.IsEmpty(batchid)) return true;
//
//        List<FiDistbatchHist> histList=fiDistbatchHistService.list(new MPJLambdaWrapper<FiDistbatchHist>()
//                .eq(FiDistbatchHist::getBatchid,batchid)
//                .orderByAsc(FiDistbatchHist::getActiondt)
//        );
//        if(JPubUtils.IsEmpty(histList))
//            return true;
//
//        boolean bret=true;
//        //加入到以前的老批次
//        FiDistbatchHist hist=histList.get(0);
//
//        Date bindenddt1=JPubUtils.addDays(hist.getActiondt(),hist.getBinddays());
//        Date bindenddt2=JPubUtils.addDays(new Date(),binddays);
//        if(JPubUtils.IsEqual(bindenddt1,bindenddt2))
//            bret=true;
//        else
//            bret=false;
//        histList.clear();
//
//        return bret;
    }

    private BigDecimal getPayAmount(Integer ficaseid)
    {//取实际还款额
        try {
            FiCreditcases fi = baseMapper.selectById(ficaseid);
            String acctno = fi.getCreditAcct();
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
            //开始时间从分案次日凌晨开始，在函数里面计算
            String distdt = sdf.format(fi.getDistdt());
            String recydt = sdf.format(new Date());
            //return baseMapper.getPayAmt(acctno, distdt, recydt);
            return new BigDecimal(0.0);
        }
        catch (Exception e){
            log.warn("getPayAmount({}).Fail:{}",ficaseid,e.getMessage());
            return new BigDecimal(0.0);
        }
    }

    @Override
    public int bindFiCastList(List<Integer> caseList,JSysUser user,CrdCaseReqVo paras)
    {
        return  bindFiCastList(caseList, user, paras, null);
    }

    private int bindFiCastList(List<Integer> caseList,JSysUser user,CrdCaseReqVo paras,DistReqVo distReqVo)
    {//2024-07-28
        //List<Integer> arr=new LinkedList<>();
        boolean iscock=JPubUtils.hasValue(paras.getProdtype(),"3");
        log.info("bindFiCastList:distMaxCount={},iscock={}",JPubUtils.distMaxCount,iscock);
        if(caseList != null && caseList.size()>0){
            if(paras.getCreditcaseidlist() == null){
                paras.setCreditcaseidlist( new ArrayList<>());
            }
            for(Integer x : caseList)
                paras.getCreditcaseidlist().add(String.valueOf(x));
        }
        caseList.clear();
        MPJLambdaWrapper<FiCreditcases> quyWrapper=getQueryWrapper(user,paras);
        if( ObjectUtils.isNotNull(distReqVo))
        {
            quyWrapper.apply(distReqVo.getExcludeBlack() == 1, " not exists(select 1 from con_blacklist cbl where cbl.customernumber = cu.customernumber and cbl.type = 1 )" )
                    .apply(distReqVo.getExcludeWhite() == 1, " not exists( select 1 from con_blacklist cbl where cbl.customernumber = cu.customernumber and cbl.type = 2 )" )
                    .apply(distReqVo.getExcludeStop() == 1, " not exists( SELECT 1 FROM fi_stopcall s WHERE s.stopkey = concat('',t.creditcaseid) and s.stoptype = 'CASE' AND s.status = 2 )")
                    .apply(distReqVo.getExcludeCustomerStop() == 1, " not exists( SELECT 1 FROM fi_stopcall s WHERE s.stopkey = t.customernumber and s.stoptype = 'CUSTOMER' AND s.status = 2 )");
        }
        IPage<CrdCaseResVo> pages=selectJoinListPage(new Page<>(1, JPubUtils.distMaxCount),
                CrdCaseResVo.class,
                quyWrapper.select(FiCreditcases::getCreditcaseid)
        );
        log.info("bindFiCastList:total={},page={},size={}",pages.getTotal(),pages.getPages(),pages.getSize());
        pages.convert(u->{
            caseList.add(u.getCreditcaseid());
            return u;
        });
        //Collections.copy(caseList,arr);
        return caseList.size();
    }

    @Override

    public CmdResVo saveAsExcel(JSysUser user, CrdCaseReqVo paras, HttpServletResponse response) {
        String $usrid = user.getUsrid();
        //int pagesize = paras.getPageSize();
        //if (pagesize > JPubUtils.exportMaxCount) {
        //    paras.setPageSize(JPubUtils.exportMaxCount);
        //}
        int pageSize = 200;
        log.info("ficase.saveAsExcel. fileename:{} ",paras.getExpfilename());

        IPage<CrdCaseResVo> pages= null; //crdCaseList(user, paras,false);
        ArrayList arr=new ArrayList();
        Integer lastId = 0;
        paras.setColumn("creditcaseid");
        paras.setIsASC(true);
        paras.setLastCaseId(lastId);
        paras.setPageNum(1);
        paras.setPageSize(pageSize);

        while(true) {
            pages = crdCaseList(user, paras,false);
            pages.convert(u -> {
                HashMap map = JPubUtils.toExportMap(u);

                if (map != null) {
//                log.info("map:{}",u);
                    // custTag 、conCustTag
                    List<SysTagsColor> custTagList = (List<SysTagsColor>) map.get("custTag");
                    List<SysTagsColor> conCustTagList = (List<SysTagsColor>) map.get("conCustTag");
                    map.put("custTag", custTagList == null ? null : custTagList.stream().map(SysTagsColor::getDsc).collect(Collectors.joining(",")));
                    map.put("conCustTag", conCustTagList == null ? null : conCustTagList.stream().map(SysTagsColor::getDsc).collect(Collectors.joining(",")));
                    arr.add(map);
                }
                return u;
            });
            if( pages.getRecords().size() == pageSize)
             {
                lastId = pages.getRecords().get(pageSize - 1).getCreditcaseid();
                paras.setLastCaseId(lastId);
            }
            else
                break;
        }
        String exptempid = JPubUtils.getExpTempID(paras.getExpfilename(), "案件表");
        String filename = JPubUtils.getExpFileName(
                paras.getExpfilename(), exptempid
        );
        CmdResVo resVo=exportService.ToExcel(
                response,
                arr,
                paras.getExpidlist(),
                paras.getExpnamelist(),
                filename,
                $usrid
        );

        int okcount=resVo.getCount();
        if(okcount>0){
            fiImportHistService.AddExportHist(
                    resVo.getKeyId(),
                    $usrid,
                    exptempid,
                    filename,
                    okcount
            );
        }
        return resVo;
    }

    @Override
    public CmdResVo getRecycleProcess()
    {
        Integer count=jCommonData.GetRecycleProcessCount();
        if(count==null||count==-1){
            return new CmdResVo(-1,"end");
        }
        return new CmdResVo(count,"running");
    }

    @Override
    public CmdResVo getOneRecyCount(JSysUser user, CrdCaseReqVo dataParas) {
        int cnt=0;
        if(dataParas!=null){
            List<Integer> recylistID = new LinkedList<>();
            cnt = bindFiCastList(recylistID, user, dataParas);
            log.info("getOneRecyCount={}", cnt);
        }
        return new CmdResVo(cnt);
    }

    @Override
    public List<FiCreditcases> getDistributeCollectList(String conditionSql)
    {
        return baseMapper.getDistributeCollectList(conditionSql);
    }

    @Override
    @Transactional
    public CmdResVo DistributeToGrpByRule(String usrId, String ruleStepId, CfgActivity cfgActivity, String sql)
    {
        //获取分配的部门:
        //分配策略，高价 25%，中间 50%，低价 25% 按照比例切割案件区间。

        List<CfgActivityOthergrplink> groups = cfgActivityOthergrplinkService.list(
                new QueryWrapper<CfgActivityOthergrplink>()
                        .eq("activityid", cfgActivity.getActivityid())
                        .gt("radio",0)
                        .orderByDesc("radio"));

        if( groups == null || groups.size() == 0)
        {
            log.info("没有找到满足条件的组织，stepid:<{}>",ruleStepId);
            return new CmdResVo(0, "没有找打满足条件的组织");
        }
        //改成直接取数。
        List<DistCreditcaseVo> distCaseList =  baseMapper.getDistObjectList(sql, cfgActivity.getAmountType());
        if( distCaseList == null || distCaseList.size() == 0){
            log.info("没有找到满足条件的条件，stepid:<{}>",ruleStepId);
            return new CmdResVo(0, "没有找打满足条件的案件");
        }
        DistCountVo distCountVo = new DistCountVo();
        distCountVo.setTotalCount(distCaseList.size());
        distCountVo.setTotalAmt(GetTotalAmount(distCaseList));
//        DistCountVo distCountVo = baseMapper.getDistTotalCount(sql);
//        if( distCountVo == null || distCountVo.getTotalCount() == 0) {
//            return new CmdResVo(0, "没有找打满足条件的案件");
//        }
//        //按照逾期结清金额计算。计算总额改成为逾期结清金额。
//        if(cfgActivity.getAmountType() == 2) {
//            distCountVo.setTotalAmt(distCountVo.getTotalWaitAmt());
//        }
        //计算回收方式。A#B#C#D
        // 升期回收#降低回收#出崔回收#结清回收#停催回收
        log.info("待分配案件：Stepid :<{}>,数量：<{}>,总金额:<{}>",ruleStepId,distCountVo.getTotalCount(),distCountVo.getTotalAmt());
        Integer pRecycleType = 0;
        if(StringUtils.isNotEmpty(cfgActivity.getRecycleType()) ){

            String[] s = cfgActivity.getRecycleType().split("#");
            pRecycleType = Integer.parseInt(s[0]) * 100 + Integer.parseInt(s[1]) * 200 + Integer.parseInt(s[2]) * 10 + Integer.parseInt(s[3]);
            if(s.length >= 5)
                pRecycleType += Integer.parseInt(s[4]) * 1000;
        }
        //计算目标
        int type = cfgActivity.getDistype2();
        List<AutoDistribute> grplist = new ArrayList<>();
        AutoDistribute model = null;

        for (int i = 0; i < (long) groups.size(); i++) {
            model = new AutoDistribute();
            model.setSysGrp(groups.get(i));
            model.setNum(0);
            model.setRatio(groups.get(i).getRadio());
            model.setAmount(BigDecimal.valueOf(0));
            model.setGrpid( groups.get(i).getGrpid());
            //下面代码没有实际含义。
            switch (type)
            {
                case 1: //数量平分。
                    model.setTargetNum( distCountVo.getTotalCount()/groups.size() + (distCountVo.getTotalCount() % groups.size() > i ? 1:0));
                    model.setTargetAmt(distCountVo.getTotalAmt().divide( BigDecimal.valueOf(groups.size())));
                    break;
                case 2: //按照金额比例
                    model.setTargetAmt(distCountVo.getTotalAmt().multiply(BigDecimal.valueOf(1.0*groups.get(i).getRadio()/100.0)));
                    model.setTargetNum( 1.0 * distCountVo.getTotalCount() * groups.get(i).getRadio()/100);
                    break;
                case 3: //按照数量比例
                    model.setTargetNum( 1.0 * distCountVo.getTotalCount() * groups.get(i).getRadio()/100);
                    model.setTargetAmt(distCountVo.getTotalAmt().multiply(BigDecimal.valueOf(1.0*groups.get(i).getRadio()/100.0)));
                    break;
            }
            grplist.add(model);
        }
        int disableLastGrp =  0;
        if ( ObjectUtils.isNull( cfgActivity.getDisablelastgrp()))
            disableLastGrp = 0;
        else
            disableLastGrp = cfgActivity.getDisablelastgrp();
        if( groups.size() == 1) {
            disableLastGrp = 0;
            type = 1;
        }
        processDistributeCnt(disableLastGrp, type, grplist, distCountVo);
        // 获取当前日期和时间
        Random random = new Random();
        int id = random.nextInt(100);
        LocalDateTime currentDateTime = LocalDateTime.now();
        String formatter = currentDateTime.format(DateTimeFormatter.ofPattern("yyMMddHHmm"));
        String batchid = "A" + formatter + String.format("%03d", id);

        Date distdt = new Date();
        Date binddt = null;
        long diffInDays = 0;

        if (Objects.equals(cfgActivity.getDistdaymode(), "1")) {
            Calendar calendar = Calendar.getInstance();
            calendar.setTime(new Date());
            calendar.add(Calendar.MONTH, cfgActivity.getAftermonth());
            calendar.set(Calendar.DATE,Integer.parseInt(cfgActivity.getFixday()));

            binddt = calendar.getTime();
            SimpleDateFormat sdf = new SimpleDateFormat("dd");
            if( Integer.parseInt(sdf.format(binddt)) != Integer.parseInt(cfgActivity.getFixday()))
            {
                log.info("Bind date={}, config date={}",sdf.format(binddt),cfgActivity.getFixday());
                //获取上一个月的最后一天日期。
                calendar.set(Calendar.DAY_OF_MONTH, 1);
                calendar.add(Calendar.DATE, -1);
                binddt = calendar.getTime();
            }
            long diffInMillies = Math.abs(calendar.getTime().getTime() - distdt.getTime());
            diffInDays = TimeUnit.DAYS.convert(diffInMillies, TimeUnit.MILLISECONDS);

        } else {
            Calendar calendar = Calendar.getInstance();
            calendar.setTime(new Date());
            calendar.add(Calendar.DATE, cfgActivity.getBindday());
            binddt = calendar.getTime();
            diffInDays = cfgActivity.getBindday();
        }
        Collections.shuffle(grplist);
        grplist.sort(Comparator.comparing(AutoDistribute::getRatio).reversed());
        Integer okcnt = 0;
        List<List<DistCreditcaseVo>> lists = new ArrayList<>();
        Integer needCount = 0;
        Integer startPos = 0;
        for( int loop=0; loop<3; loop++) {
            if (loop == 0)
                needCount = distCountVo.getTargetCount1();
            else if (loop == 1)
                needCount = distCountVo.getTargetCount2();
            else
                needCount = distCountVo.getTargetCount3();
            //List<DistCreditcaseVo> objLists =  baseMapper.getDistObjectList(startPos, needCount, sql, cfgActivity.getAmountType());
            List<DistCreditcaseVo> objLists =  getDistObject(startPos, needCount, distCaseList);
            log.info("DistributeToGrpByRule getDistObjectList: stepid:{}, loop：{}, startpos:{},needcount:{}, selectedcount:{}",
                    ruleStepId, loop, startPos, needCount, objLists.size());
            lists.add(objLists);
            startPos += needCount;
        }

        for( int loop=0; loop < 3; loop++) {
            List<DistCreditcaseVo> distCreditcaseVos = lists.get(loop);
            log.debug("Start distribute loop:{},distcount:{}",loop, (ObjectUtils.isNotNull(distCreditcaseVos) ? distCreditcaseVos.size() : 0));
            if( distCreditcaseVos == null || distCreditcaseVos.size() == 0)
                continue;
            Collections.shuffle(distCreditcaseVos);
            for (int grpIndex = 0; grpIndex < grplist.size(); grpIndex++) {
                grplist.get(grpIndex).setDistNum(0);
            }
            for (DistCreditcaseVo x : distCreditcaseVos) {
                //if (firstSort)
                //    firstSort = false;
                //else if (grplist.size() > 1)
                //    grplist.sort(Comparator.comparing(AutoDistribute::getOverRatio));
                boolean distributed = false;
                for (int grpIndex = 0; grpIndex < grplist.size(); grpIndex++) {

                    if( grplist.get(grpIndex).getTargetDistNum()[loop] == 0)
                            continue;
                    else if(grplist.get(grpIndex).getDistNum() >= grplist.get(grpIndex).getTargetDistNum()[loop])
                            continue;
                    if( disableLastGrp == 1 && StringUtils.isNotEmpty(x.getLastSendToDept()) && grplist.get(grpIndex).getGrpid().compareTo(x.getLastSendToDept())==0)
                            continue;
                    x.setDistGrpId(grplist.get(grpIndex).getGrpid());
                    grplist.get(grpIndex).setNum(grplist.get(grpIndex).getNum() + 1);
                    grplist.get(grpIndex).setDistNum(grplist.get(grpIndex).getDistNum() + 1);
                    grplist.get(grpIndex).setAmount(grplist.get(grpIndex).getAmount().add(x.getOverdueTotalamt()));
                    grplist.get(grpIndex).getDistCreditcaseVos().add(x);
                    distributed = true;
                    break;
                }
                //出现无法分配的记录
                if( !distributed ){
                    for (AutoDistribute autoDistribute : grplist) {
                        autoDistribute.setOverRatio(1.0 * autoDistribute.getNum()/autoDistribute.getTargetNum());
                    }
                    grplist.sort(Comparator.comparing(AutoDistribute::getOverRatio));
                    for (AutoDistribute autoDistribute : grplist) {
                        if( autoDistribute.getGrpid().equals(x.getLastSendToDept()))
                            continue;
                        autoDistribute.setNum(autoDistribute.getNum() + 1);
                        autoDistribute.setAmount(autoDistribute.getAmount().add(x.getOverdueTotalamt()));
                        x.setDistGrpId(autoDistribute.getGrpid());
                        autoDistribute.getDistCreditcaseVos().add(x);
                        break;
                    }
                }
                //okcnt += updateAutoDistCreditCase(usrId, x, ruleStepId, cfgActivity, batchid, distdt, binddt, 1, pRecycleType);
                log.debug("Stepid:<{}> ,caseid:<{}>, lastgrpid:{}, ditgrp:{}",
                        ruleStepId,x.getCreditcaseid(),x.getLastSendToDept(),x.getDistGrpId());
            }
            distCreditcaseVos.clear();
        }
        //计算每个歪歪机构的比例，确认是否需要调整。
        for (AutoDistribute autoDistribute : grplist) {
            if(autoDistribute.getTargetAmt().equals(BigDecimal.ZERO))
                autoDistribute.setOverRatio(1.0);
            else
                autoDistribute.setOverRatio(1.0 * autoDistribute.getAmount().doubleValue()/autoDistribute.getTargetAmt().doubleValue());
        }
        while(true) {
            grplist.sort(Comparator.comparing(AutoDistribute::getOverRatio).reversed());
            if (grplist.get(0).getOverRatio() > 1.05 || grplist.get(grplist.size()-1).getOverRatio() < 0.95) {
                //调整 调转方案，最大比例和最小比例间调转。
                //首先队列排序
                grplist.get(0).getDistCreditcaseVos().sort(Comparator.comparing(DistCreditcaseVo::getOverdueTotalamt).reversed());
                grplist.get(grplist.size()-1).getDistCreditcaseVos().sort(Comparator.comparing(DistCreditcaseVo::getOverdueTotalamt).reversed());

                int ret = swapCase(disableLastGrp, grplist.get(0),grplist.get(grplist.size()-1));
                if( ret < 0 ){
                    log.info("swap Error: ret={},exit.",ret);
                    break;
                }
            }
            else
                break;
        }
        //更新分案案件记录。
        for (AutoDistribute autoDistribute : grplist)
        {
            for(DistCreditcaseVo x : autoDistribute.getDistCreditcaseVos())
            {
                x.setDistGrpId(autoDistribute.getGrpid());
                okcnt += updateAutoDistCreditCase(usrId, x, ruleStepId, cfgActivity, batchid, distdt, binddt, 1, pRecycleType);
            }
        }
        FiDistbatchHist fiDistbatchHist = new FiDistbatchHist();
        fiDistbatchHist.setBatchid(batchid);
        fiDistbatchHist.setBatchname(cfgActivity.getName() + batchid);
        fiDistbatchHist.setBinddays((int) diffInDays);
        fiDistbatchHist.setBindenddt(binddt);
        fiDistbatchHist.setActiontype("1");
        fiDistbatchHist.setActiondt(new Date());
        fiDistbatchHist.setActionusr(usrId);
        fiDistbatchHist.setOkcnt(okcnt);
        fiDistbatchHist.setDistcnt(distCountVo.getTotalCount());
        fiDistbatchHist.setDistamt(distCountVo.getTotalAmt());
        fiDistbatchHist.setDistmode(cfgActivity.getDistype2() == 1 ? 1 : 5);
        fiDistbatchHistService.save(fiDistbatchHist);
        baseMapper.updateToHist(batchid,new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(distdt));
        //清空
        distCaseList.clear();
        return new CmdResVo(distCountVo.getTotalCount(),"分案完成");

    }

    /**
     * 交换案件
     * @param disableLastGrp
     * @param highGrp
     * @param lowGrp
     * @return
     */
    private Integer swapCase(Integer disableLastGrp, AutoDistribute highGrp, AutoDistribute lowGrp)
    {
        if( lowGrp.getDistCreditcaseVos().size() == 0)
            return -1;
        Integer highIndex = 0;
        Integer lowIndex = -1;
        DistCreditcaseVo x1 = null;
        DistCreditcaseVo x2 = null;
        for( int k=0; k< highGrp.getDistCreditcaseVos().size(); k++) {
            x1 = highGrp.getDistCreditcaseVos().get(k);
            highIndex = k;
            lowIndex = -1;
            for (int i = 0; i < lowGrp.getDistCreditcaseVos().size(); i++) {
                x2 = lowGrp.getDistCreditcaseVos().get(lowGrp.getDistCreditcaseVos().size() - i - 1);
                log.info("Swap high distgrpid:{}, caseid:{}, amount{}, overratio:{}, " +
                                "to low distgrpid:{}, csaeid:{}, amount{},overratio:{}",
                        highGrp.getGrpid(), x1.getCreditcaseid(), x1.getOverdueTotalamt(), highGrp.getOverRatio(),
                        lowGrp.getGrpid(), x2.getCreditcaseid(), x2.getOverdueTotalamt(), lowGrp.getOverRatio());

                if (disableLastGrp == 1 && StringUtils.isNotEmpty(x1.getLastSendToDept()) && x1.getLastSendToDept().compareTo(lowGrp.getGrpid()) == 0) {
                    continue;
                }
                else if (disableLastGrp == 1 && StringUtils.isNotEmpty(x2.getLastSendToDept()) && x2.getLastSendToDept().compareTo(highGrp.getGrpid()) == 0) {
                    continue;
                }
                else if(x1.getOverdueTotalamt().compareTo(x2.getOverdueTotalamt()) <=0) {
                    lowIndex = -2;
                    break;
                }
                else if (1.0 * lowGrp.getAmount()
                        .add(x1.getOverdueTotalamt())
                        .subtract(x2.getOverdueTotalamt())
                        .doubleValue() / lowGrp.getTargetAmt().doubleValue() > 1.05) {
                    lowIndex = -1;
                    break;
                }
                else if (1.0 * highGrp.getAmount()
                        .add(x2.getOverdueTotalamt())
                        .subtract(x1.getOverdueTotalamt())
                        .doubleValue() / highGrp.getTargetAmt().doubleValue() < 0.95) {
                    continue;
                }
                else {
                    lowIndex = i;
                    break;
                }
            }
            if( lowIndex == -1)
                continue;
            else if( lowIndex == -2)
                return -1;
            else
                break;
        }
        if( lowIndex == -1)
            return -1;
        highGrp.getDistCreditcaseVos().remove(x1);
        highGrp.getDistCreditcaseVos().add(x2);
        highGrp.setAmount(highGrp.getAmount().subtract(x1.getOverdueTotalamt()));
        highGrp.setAmount(highGrp.getAmount().add(x2.getOverdueTotalamt()));
        highGrp.setOverRatio(1.0 * highGrp.getAmount().doubleValue()/highGrp.getTargetAmt().doubleValue());
        lowGrp.getDistCreditcaseVos().remove(x2);
        lowGrp.getDistCreditcaseVos().add(x1);
        lowGrp.setAmount(lowGrp.getAmount().subtract(x2.getOverdueTotalamt()));
        lowGrp.setAmount(lowGrp.getAmount().add(x1.getOverdueTotalamt()));
        lowGrp.setOverRatio(1.0 * lowGrp.getAmount().doubleValue()/lowGrp.getTargetAmt().doubleValue());
        log.info("Swap OK, high grpid:{},remove:<{}>,add:<{}>, overratio:{}, low grpid:{},overratio:{}",
                highGrp.getGrpid(),x1.getCreditcaseid(),x2.getCreditcaseid(), highGrp.getOverRatio(),lowGrp.getGrpid(),lowGrp.getOverRatio());
        return 0;
    }

    private Integer swapCase(Integer disableLastGrp, DistResVo highGrp,DistResVo lowGrp)
    {
        //DistResVo highGrp = grplist.get(0);
        //DistResVo lowGrp = grplist.get(grplist.size() -1);
        Integer highIndex = 0;
        Integer lowIndex = -1;
        DistCreditcaseVo x1 = null;
        DistCreditcaseVo x2 = null;
        if( lowGrp.getFicaseList().size() == 0)
            return -1;
        for( int k=0; k< highGrp.getFicaseList().size(); k++) {
            x1 = highGrp.getFicaseList().get(k);
            highIndex = k;
            lowIndex = -1;
            for (int i = 0; i < lowGrp.getFicaseList().size(); i++) {
                x2 = lowGrp.getFicaseList().get(lowGrp.getFicaseList().size() - i - 1);
                log.info("Swap high distgrpid:{}, caseid:{}, amount{}, overratio:{}, " +
                                "to low distgrpid:{}, csaeid:{}, amount{},overratio:{}",
                        highGrp.getKey(), x1.getCreditcaseid(), x1.getOverdueTotalamt(), highGrp.getOverRatio(),
                        lowGrp.getKey(), x2.getCreditcaseid(), x2.getOverdueTotalamt(), lowGrp.getOverRatio());

                if (disableLastGrp == 1 && StringUtils.isNotEmpty(x1.getLastSendToDept()) && x1.getLastSendToDept().compareTo(lowGrp.getKey()) == 0) {
                    continue;
                }
                else if (disableLastGrp == 1 && StringUtils.isNotEmpty(x2.getLastSendToDept()) && x2.getLastSendToDept().compareTo(highGrp.getKey()) == 0) {
                    continue;
                }
                else if(x1.getOverdueTotalamt().compareTo(x2.getOverdueTotalamt()) <=0) {
                    log.info("HighGrp Case Amount:{} <= {} LowGrp case Amount, swap fail.",
                            x1.getOverdueTotalamt(),x2.getOverdueTotalamt());
                    lowIndex = -2;
                    break;
                }
                else if (1.0 * (lowGrp.getAmount()
                        .add(x1.getOverdueTotalamt())
                        .subtract(x2.getOverdueTotalamt()))
                        .doubleValue() / lowGrp.getPreamount().doubleValue() > 1.05) {
                    log.info("if swap Lowgrp ratio={} > 1.05, break.", 1.0 * (lowGrp.getAmount()
                            .add(x1.getOverdueTotalamt())
                            .subtract(x2.getOverdueTotalamt()))
                            .doubleValue() / lowGrp.getPreamount().doubleValue());
                    lowIndex = -1;
                    break;
                }
                else if (1.0 * (highGrp.getAmount()
                        .add(x2.getOverdueTotalamt())
                        .subtract(x1.getOverdueTotalamt()))
                        .doubleValue() / highGrp.getPreamount().doubleValue() < 0.95) {
                    log.info("if swap highGrp ratio={}<0.95 ,continue", 1.0 * (highGrp.getAmount()
                            .add(x2.getOverdueTotalamt())
                            .subtract(x1.getOverdueTotalamt()))
                            .doubleValue() / highGrp.getPreamount().doubleValue());
                    continue;
                }
                else {
                    lowIndex = i;
                    break;
                }
            }
            if (lowIndex == -1)
                continue;
            else if(lowIndex == -2)
                return -1;
            else
                break;
        }
        if( lowIndex == -1) {
            log.info("No find can swap case. swap failed, highGrp:{}, lowGrp:{}", highGrp.getKey(),lowGrp.getKey());
            return -1;
        }
        highGrp.getFicaseList().remove(x1);
        highGrp.getFicaseList().add(x2);
        highGrp.setAmount(highGrp.getAmount().subtract(x1.getOverdueTotalamt()));
        highGrp.setAmount(highGrp.getAmount().add(x2.getOverdueTotalamt()));
        highGrp.setOverRatio(1.0 * highGrp.getAmount().doubleValue()/highGrp.getPreamount().doubleValue());
        lowGrp.getFicaseList().remove(x2);
        lowGrp.getFicaseList().add(x1);
        lowGrp.setAmount(lowGrp.getAmount().subtract(x2.getOverdueTotalamt()));
        lowGrp.setAmount(lowGrp.getAmount().add(x1.getOverdueTotalamt()));
        lowGrp.setOverRatio(1.0 * lowGrp.getAmount().doubleValue()/lowGrp.getPreamount().doubleValue());
        log.info("Swap OK, high grpid:{}, overratio:{}, low grpid:{},overratio:{}",
                highGrp.getKey(),highGrp.getOverRatio(),lowGrp.getKey(),lowGrp.getOverRatio());
        return 0;
    }

    @Override
    public CmdResVo getNextFiCaseID(JSysUser user, Integer ficaseid) {
        String usrid=user.getUsrid();
        String key=jCommonData.getQuyNextCaseDataKey(usrid);
        Object o=jCommonData.getData(key);
        if(o==null){
            log.warn("getNextFiCaseID({}):nofinddata={}",ficaseid,key);
            return new CmdResVo(0);
        }
        boolean bnext=false;
        Integer nextcaseid=0;
        IPage<CrdCaseResVo> pages=(IPage<CrdCaseResVo>)o;
        List<CrdCaseResVo> ll=pages.getRecords();
        for(CrdCaseResVo p : ll){
            if(p.getCreditcaseid().equals(ficaseid)){
                bnext=true;
            }
            else if(bnext){
                nextcaseid=p.getCreditcaseid();
                log.info("getNextFiCaseID({})->{}",ficaseid,p.getCreditcaseid());
                break;
            }
        }
        if(nextcaseid==0){//下一页
            log.info("getNextFiCaseID({}):find next page",ficaseid);
            key=jCommonData.getQuyNextCaseKey(usrid);
            o=jCommonData.getData(key);
            if(o==null){
                log.warn("getNextFiCaseID:nofindquypara={}",key);
                return new CmdResVo(0);
            }
            CrdCaseReqVo reqVo=(CrdCaseReqVo)o;
            int currpagenum=reqVo.getPageNum();
            reqVo.setPageNum(currpagenum+1);

            boolean b=jCommonData.setData(key,reqVo,JPubUtils.distExpiration*2);
            log.info("getNextFiCaseID({}):pagenum={},nextpagetnum={},ret={}",
                    ficaseid,currpagenum,reqVo.getPageNum(),b
            );
            IPage<CrdCaseResVo> quyResult=crdCaseList(user,reqVo,true);
            ll=quyResult.getRecords();
            if(ll.size()<=0){
                log.info("getNextFiCaseID({}):quydata has last",ficaseid);
                return new CmdResVo(0);
            }
            nextcaseid=ll.get(0).getCreditcaseid();
            key=jCommonData.getQuyNextCaseDataKey(usrid);
            jCommonData.setData(key,quyResult,JPubUtils.distExpiration*2);
        }

        return new CmdResVo(1,nextcaseid.toString());
    }

    @Override
    public CmdResVo getPrevFiCaseID(JSysUser user, Integer ficaseid) {
        String usrid=user.getUsrid();
        String key=jCommonData.getQuyNextCaseDataKey(usrid);
        Object o=jCommonData.getData(key);
        if(o==null){
            log.warn("getPrevFiCaseID({}):nofinddata={}",ficaseid,key);
            return new CmdResVo(0);
        }
        Integer prevcaseid=0;
        IPage<CrdCaseResVo> pages=(IPage<CrdCaseResVo>)o;
        List<CrdCaseResVo> ll=pages.getRecords();
        for(CrdCaseResVo p : ll){
            log.info("ficaseid:{},getCreditcaseid():{}",ficaseid,p.getCreditcaseid());
            if(p.getCreditcaseid().equals(ficaseid)){
                break;
            }
            prevcaseid=p.getCreditcaseid();
        }
        log.info("getPrevFiCaseID({}):find.prevcaseid={}",ficaseid,prevcaseid);
        if(prevcaseid==0){//上一页
            log.info("getPrevFiCaseID({}):find prev page",ficaseid);
            key=jCommonData.getQuyNextCaseKey(usrid);
            o=jCommonData.getData(key);
            if(o==null){
                log.warn("getPrevFiCaseID:nofindquypara={}",key);
                return new CmdResVo(0);
            }
            CrdCaseReqVo reqVo=(CrdCaseReqVo)o;
            int currpagenum=reqVo.getPageNum();
            if(currpagenum<=1){
                log.info("getPrevFiCaseID({}):page has first",ficaseid);
                return new CmdResVo(0);
            }

            reqVo.setPageNum(currpagenum-1);

            boolean b=jCommonData.setData(key,reqVo,JPubUtils.distExpiration*2);
            log.info("getPrevFiCaseID({}):pagenum={},prevpagetnum={},ret={}",
                    ficaseid,currpagenum,reqVo.getPageNum(),b
            );
            IPage<CrdCaseResVo> quyResult=crdCaseList(user,reqVo,true);
            ll=quyResult.getRecords();
            if(ll.size()<=0){
                log.info("getPrevFiCaseID({}):quydata has first",ficaseid);
                return new CmdResVo(0);
            }
            prevcaseid=ll.get(ll.size()-1).getCreditcaseid();
            key=jCommonData.getQuyNextCaseDataKey(usrid);
            jCommonData.setData(key,quyResult,JPubUtils.distExpiration*2);
        }

        return new CmdResVo(1,prevcaseid.toString());
    }


//    private List<DistCreditcaseVo> getDistCaseObject(Iterator it1, Long needCount, Boolean isDesc)
//    {
//            List<DistCreditcaseVo> distCreditcaseVos  = new ArrayList<>();
//            while( needCount-- > 0 && it1.hasNext() )
//            {
//                distCreditcaseVos.add((DistCreditcaseVo)it1.next());
//            }
//            if( !isDesc)
//                distCreditcaseVos.sort(Comparator.comparing(DistCreditcaseVo::getOverdueTotalamt).reversed());
//            return distCreditcaseVos;
//    }
    private Integer  updateAutoDistCreditCase(String usrId,
                                               DistCreditcaseVo x,
                                               String ruleStepId,
                                               CfgActivity cfgActivity,
                                               String batchid,
                                               Date distDt,
                                               Date binddt,
                                               Integer distributeType,
                                               Integer pRecycleType)
    {
        try {
            update(null,new UpdateWrapper<FiCreditcases>()
                    .lambda()
                    .eq(FiCreditcases::getCreditcaseid, x.getCreditcaseid())
                    .set(FiCreditcases::getStatus, EnumFiCaseStatus.Distribute.value)
                    .set(FiCreditcases::getStatusdt, distDt)
                    .set(FiCreditcases::getCcc, EnumFiCaseQueue.Q.value)
                    .set(FiCreditcases::getDistbatchid,batchid)
                    .set(FiCreditcases::getDistdt, distDt)
                    .set(FiCreditcases::getDistusr, usrId)
                    .set(FiCreditcases::getDistributetype, distributeType)
                    .set(FiCreditcases::getDistributorid, StringUtils.isNotEmpty(ruleStepId) ? Long.parseLong(ruleStepId): null) //cfgActivity.getActivityid())
                    .set(FiCreditcases::getDisttag, cfgActivity.getCaseflag())
                    .set(FiCreditcases::getMddt,new Date())
                    .set(FiCreditcases::getMdusr,usrId)
                    .set(FiCreditcases::getBindenddt, binddt)
                    .set(FiCreditcases::getRecycleType, pRecycleType)
                    .set(FiCreditcases::getCpStartTime,
                            ObjectUtils.isNotNull(cfgActivity.getEffectivedistday()) ? cfgActivity.getEffectivedistday(): distDt)
                    .set(FiCreditcases::getCpEndTime, binddt)
                    .set(FiCreditcases::getCpAmt,
                            cfgActivity.getAmountType() == 1 ? x.getPreRepayPrincipal() : x.getPreRepayPrincipal().add(x.getOdudPrin()))
                    .set(FiCreditcases::getCpCost,x.getPreRepayFee())
                    .set(FiCreditcases::getCpInterest,
                            cfgActivity.getAmountType() == 1 ? x.getPreRepayInterest() : x.getPreRepayInterest().add(x.getInterestFee()))
                    .set(FiCreditcases::getCpTotalamt,
                            cfgActivity.getAmountType() == 1 ? x.getOverdueTotalamt() : x.getWaitAmount())
                    .set(FiCreditcases::getCpOverdueDays,x.getMthsDay())
                    .set(FiCreditcases::getSendtodept,x.getDistGrpId())
                    .set(FiCreditcases::getRepay,null)
                    .set(FiCreditcases::getRepaydt,null)
                    .set(FiCreditcases::getAssignid,null)
                    .set(FiCreditcases::getAssignbatchid,null)
                    .set(FiCreditcases::getAssigndt,null)
                    .set(FiCreditcases::getAssignusr,null)
                    .set(FiCreditcases::getSendtousr,null)
                    .set(FiCreditcases::getAssigntype,null)
                    .set(FiCreditcases::getRepaidAmount,null)  //2025-07-02 清空。
            );
            //记录委案是的借据的逾期天数。
            iouLoanInvoiceInfoService.update( null,
                    new LambdaUpdateWrapper<IouLoanInvoiceInfo>()
                            .eq(IouLoanInvoiceInfo::getCreditcaseid,x.getCreditcaseid())
                            .setSql("cp_overdue_days = overdue_days"));
            return 1;
        }catch(Exception e)
        {
            log.trace("CreditCaseId:<" + x.getCreditcaseid() + "> auto distribute update error:"+e.toString());
            return 0;
        }
    }

    void processDistributeCnt(Integer disableLastGrp, Integer distMode, List<AutoDistribute> grplist, DistCountVo distCountVo)
    {
        //按照比例
        log.info( "待分配数量：{}, 机构数量：{},分配模式：{}，是否禁分配上次组织：{}",distCountVo.getTotalCount(),
                grplist.size(), distMode, disableLastGrp);
        if( distMode == 1){
            distCountVo.setTargetCount1(distCountVo.getTotalCount());
            distCountVo.setTargetCount2(0);
            distCountVo.setTargetCount3(0);
            for (int i = 0; i < grplist.size(); i++) {
                grplist.get(i).setTargetNum1(distCountVo.getTotalCount()/grplist.size() + ((distCountVo.getTotalCount() % grplist.size()) > i ? 1: 0 ));
                log.debug( "第一轮组织：{}，分配数量：{}", grplist.get(i).getGrpid(), grplist.get(i).getTargetNum1());
                grplist.get(i).setTargetNum2(0);
                grplist.get(i).setTargetNum3(0);
            }
        }
        else {
            Integer distCnt=0, totalDistCnt1=0, totalDistCnt2=0, totalDistCnt3 = 0, totalRatio = 0;

            for (int i = 0; i < grplist.size(); i++) {

                totalRatio += grplist.get(i).getRatio();
            }
            //检查分成三段的比例。
            int ratio1 = 0, ratio2 =0, ratio3=0;

            grplist.sort(Comparator.comparing(AutoDistribute::getRatio));
            int minRatio = grplist.get(0).getRatio();
            if( minRatio * distCountVo.getTotalCount() / totalRatio >=4 ) {
                ratio1 = 25;
                ratio2 = 50;
                ratio3 = 25;
            }
            else if( minRatio * distCountVo.getTotalCount() / totalRatio >=3 ){
                //30% 40%, 30%
                ratio1 = 30;
                ratio2 = 40;
                ratio3 = 30;
            }
            else if( minRatio * distCountVo.getTotalCount() / totalRatio >=2 ){
                ratio1 = 50;
                ratio2 = 50;
                ratio3 = 0;
            }
            else {
                // 100%
                ratio1 = 100;
                ratio2 = 0;
                ratio3 = 0;
            }
            Integer targetCnt1 = distCountVo.getTotalCount() * ratio1 / 100;
            Integer targetCnt2 = distCountVo.getTotalCount() * ratio2 / 100;
            Integer targetCnt3 = distCountVo.getTotalCount() - targetCnt1 - targetCnt2;

            if( disableLastGrp == 1)
            {
                targetCnt1 = distCountVo.getTotalCount();
                targetCnt2 = 0;
                targetCnt3 = 0;
            }
            if( targetCnt3 == 1){
                targetCnt2 += 1;
                targetCnt3 = 0;
            }
            distCountVo.setTargetCount1(targetCnt1);
            log.debug( "第一轮待分配的总数：" + targetCnt1);
            //第一轮 25%
            distCountVo.setTargetCount1(targetCnt1);

            for (int i = 0; i < grplist.size(); i++) {

                distCnt = targetCnt1 * grplist.get(i).getRatio()/totalRatio;
                grplist.get(i).setDistDivideNum( 1.0 * targetCnt1 * grplist.get(i).getRatio()/totalRatio - distCnt);
                grplist.get(i).setTargetNum1(distCnt);
                log.debug( "第一轮 组织：{}，比例：{}，计划分配数量：{}, 余数：{}",
                        grplist.get(i).getGrpid(),grplist.get(i).getRatio(), distCnt, grplist.get(i).getDistDivideNum());
                totalDistCnt1 += distCnt;
            }
            //处理余数
            grplist.sort(Comparator.comparing(AutoDistribute::getDistDivideNum).reversed());
            for (int i = 0; i < targetCnt1 - totalDistCnt1; i++) {
                grplist.get(i).setTargetNum1(grplist.get(i).getTargetNum1() + 1);
                log.debug( "第一轮组织：{}，调后分配数量：{}", grplist.get(i).getGrpid(), grplist.get(i).getTargetNum1());
            }

            distCountVo.setTargetCount2(targetCnt2);
            log.debug( "第二轮待分配的总数：" + targetCnt2);
            //第二轮 50%
            for (int i = 0; i < grplist.size(); i++) {

                distCnt = targetCnt2 * grplist.get(i).getRatio()/totalRatio;
                grplist.get(i).setDistDivideNum( 1.0 * targetCnt2 * grplist.get(i).getRatio()/totalRatio - distCnt);
                grplist.get(i).setTargetNum2(distCnt);
                log.debug( "第二轮组织：{}，比例：{}，分配数量：{}，余数：{}", grplist.get(i).getGrpid(),
                        grplist.get(i).getRatio(),distCnt, grplist.get(i).getDistDivideNum());
                totalDistCnt2 += distCnt;
            }
            //处理余数
            grplist.sort(Comparator.comparing(AutoDistribute::getDistDivideNum).reversed());
            for (int i = 0; i < targetCnt2 - totalDistCnt2; i++) {
                grplist.get(i).setTargetNum2(grplist.get(i).getTargetNum2() + 1);
                log.debug( "第二轮组织：{}，计划分配数量：{}", grplist.get(i).getGrpid(), grplist.get(i).getTargetNum2());
            }
            //第三轮 25%
            distCountVo.setTargetCount3(targetCnt3);
            log.debug( "第三轮待分配的总数：" + targetCnt3);
            for (int i = 0; i < grplist.size(); i++) {
                distCnt = targetCnt3 * grplist.get(i).getRatio()/totalRatio;
                grplist.get(i).setDistDivideNum( 1.0 * targetCnt3 * grplist.get(i).getRatio()/totalRatio - distCnt);
                grplist.get(i).setTargetNum3(distCnt);
                log.debug( "第三轮组织：{}，比例：{}，分配数量：{}, 余数：{}", grplist.get(i).getGrpid(),
                        grplist.get(i).getRatio(),distCnt, grplist.get(i).getDistDivideNum());
                totalDistCnt3 += distCnt;
            }
            //处理余数
            grplist.sort(Comparator.comparing(AutoDistribute::getDistDivideNum).reversed());
            for (int i = 0; i < targetCnt3 - totalDistCnt3; i++) {
                grplist.get(i).setTargetNum3(grplist.get(i).getTargetNum3() + 1);
                log.debug( "第三轮组织：{}，调整后分配数量：{}", grplist.get(i).getGrpid(), grplist.get(i).getTargetNum3());
            }
        }
        for (int i = 0; i < grplist.size(); i++) {
            grplist.get(i).setTargetDistNum(new Integer[]{grplist.get(i).getTargetNum1(),grplist.get(i).getTargetNum2(),grplist.get(i).getTargetNum3()});
        }
    }
    @Override
    public List<FiCreditcases> getAssignCollectlist(String conditionSql,String grpId)
    {
        return baseMapper.getAssignCollectlist(conditionSql, grpId);
    }
    @Override
    public CmdResVo addCollectGrpTag(CfgRuleTaskSteps cfgRuleTaskSteps,String sql)
    {
        if(cfgRuleTaskSteps == null ||
                !cfgRuleTaskSteps.getActionCode().equals(EnumRuleStepAction.ADDCOLLECTTAG.getCode()))
            return new CmdResVo(0,"参数错误");
        if( StringUtils.isEmpty(cfgRuleTaskSteps.getTag() ))
            return new CmdResVo(0,"Tag参数错误");
        //删除标签
//        String sql = cfgRuleTaskSteps.getFilterSql();
//        if(StringUtils.isEmpty(sql))
//            sql = " 1=1 ";
//        //客户黑名单。
//        if( cfgRuleTaskSteps.getExcludeBlack() == 1)
//            sql += " and not exists (select 1 from con_blacklist cbl where cbl.customernumber = cu.customernumber and cbl.type = 1 )";
//
//        if( cfgRuleTaskSteps.getExcludeWhite() == 1)
//            sql += " and not exists ( select 1 from con_blacklist cbl where cbl.customernumber = cu.customernumber and cbl.type = 2 )";
//
//        if( cfgRuleTaskSteps.getExcludeStop() == 1)
//            //sql += " and fi_hasstopcall(fc.creditcaseid,'CASE')=0 ";
//            sql += "  and  not exists( SELECT 1  \n" +
//                    "    FROM fi_stopcall s  \n" +
//                    "    WHERE s.stopkey = concat('',fc.creditcaseid) and s.stoptype = 'CASE' AND s.status = 2 )";
//        if(cfgRuleTaskSteps.getExcludeCustomerStop() == 1)
//            //sql += " and fi_hasstopcall(cu.customernumber,'CUSTOMER')=0 ";
//            sql += "  and  not exists( SELECT 1  \n" +
//                    "    FROM fi_stopcall s  \n" +
//                    "    WHERE s.stopkey = cu.customernumber and s.stoptype = 'CUSTOMER' AND s.status = 2 )";

        // addtagtype 1：不删除，2：删除当前客户的全部标签，3：删除全部客户的当前标签
        if( ObjectUtils.isNull(cfgRuleTaskSteps.getAddTagType()) ||  cfgRuleTaskSteps.getAddTagType() == 1) {
            //删除 满足条件的所有标签。
            fiCreditcaseGrpTagService.deleteCollectGrpTag(sql, cfgRuleTaskSteps.getTag());
        }
        else if(cfgRuleTaskSteps.getAddTagType() == 2) {        //删除 满足条件的所有标签。
            fiCreditcaseGrpTagService.deleteCollectGrpTag(sql,"");
        }
        else if(cfgRuleTaskSteps.getAddTagType() == 3) // 删除该标签的所有客户。
        {
            fiCreditcaseGrpTagService.deleteCollectGrpTag("1=1",cfgRuleTaskSteps.getTag());

        }
        fiCreditcaseGrpTagService.addCollectGrpTag(sql, cfgRuleTaskSteps.getTag());
        return new CmdResVo(1);
    }
    @Override
    public CmdResVo removeCollectGrpTag(CfgRuleTaskSteps cfgRuleTaskSteps,String sql)
    {
        if(cfgRuleTaskSteps == null ||
                !cfgRuleTaskSteps.getActionCode().equals(EnumRuleStepAction.DELETECOLLECTTAG.getCode()))
            return new CmdResVo(0,"参数错误");
        if( StringUtils.isEmpty(cfgRuleTaskSteps.getTag() ))
            return new CmdResVo(0,"Tag参数错误");
        //删除标签
//        String sql = cfgRuleTaskSteps.getFilterSql();
//        if(StringUtils.isEmpty(sql))
//            sql = " 1=1 ";
//        //客户黑名单。
//        if( cfgRuleTaskSteps.getExcludeBlack() == 1)
//            sql += " and not exists (select 1 from con_blacklist cbl where cbl.customernumber = cu.customernumber and cbl.type = 1 )";
//
//        if( cfgRuleTaskSteps.getExcludeWhite() == 1)
//            sql += " and not exists ( select 1 from con_blacklist cbl where cbl.customernumber = cu.customernumber and cbl.type = 2 )";
//
//        if( cfgRuleTaskSteps.getExcludeStop() == 1)
//            //sql += " and fi_hasstopcall(fc.creditcaseid,'CASE')=0 ";
//            sql += "  and  not exists( SELECT 1  \n" +
//                    "    FROM fi_stopcall s  \n" +
//                    "    WHERE s.stopkey = concat('',fc.creditcaseid) and s.stoptype = 'CASE' AND s.status = 2 )";
//        if(cfgRuleTaskSteps.getExcludeCustomerStop() == 1)
//            //sql += " and fi_hasstopcall(cu.customernumber,'CUSTOMER')=0 ";
//            sql += "  and  not exists( SELECT 1  \n" +
//                    "    FROM fi_stopcall s  \n" +
//                    "    WHERE s.stopkey = cu.customernumber and s.stoptype = 'CUSTOMER' AND s.status = 2 )";

        fiCreditcaseGrpTagService.deleteCollectGrpTag(sql, cfgRuleTaskSteps.getTag());

        return  new CmdResVo(1);

    }
    @Override
    public List<AssistListResVo> getAssistCaseList(List<Integer> ficaseids)
    {
        return selectJoinList(AssistListResVo.class,
                new MPJLambdaWrapper<FiCreditcases>()
                        .leftJoin(" fi_creditcases_hist_link fl on fl.creditcaseid= t.creditcaseid")
                        .leftJoin("fi_creditcase_assistant fca on fca.ficaseid = t.creditcaseid and fca.status =1")
                        .in(FiCreditcases::getCreditcaseid,ficaseids)
                        .selectAs(FiCreditcases::getCreditcaseid,AssistListResVo::getFicaseid)
                        .selectAs(FiCreditcases::getSendtodept,AssistListResVo::getSendtodept)
                        .selectAs(FiCreditcases::getSendtousr,AssistListResVo::getSendtousr)
                        .selectAs(FiCreditcases::getStatus,AssistListResVo::getCasestatus)
                        .selectAs(FiCreditcases::getCpEndTime,AssistListResVo::getCpEndTime)
                        .selectAs("fl.assignhistid",AssistListResVo::getAssignhistid)
                        .selectAs("fca.status",AssistListResVo::getStatus)
        );
    }
}




